/*
* This is a general purpose editing function. It returns a string.
* by: James Willie (jwillie@werple.apana.org.au)
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pc.h>
#include <keys.h>
#include <gppconio.h>
#include "mlj_edit.h"
#define BUFFER_LINES 400 /* Max number if lines read in */
#define LINELEN 100 /* How much allocate for each line */
#define START_ROW 3
#define SCREEN(X) (X+START_ROW-top_row) /* map buffer row to screen */
#define bot_row (top_row+20)
static void beep();
static void insert_line(int r);
static void delete_line(int r);
static void init_buffer();
static void string2buffer(char *s);
static void buffer2string(char *s);
static void handle_input();
static void show_info();
static void do_redraw_screen();
static void do_draw_header();
static void help_screen();
unsigned char word_wrap;
static int aborted;
static int row;
static int col;
static int max_row;
static int top_row;
static int store_row;
static int store_col;
static short store_screen[4096];
static char buffer[BUFFER_LINES][LINELEN];
static unsigned char cols[BUFFER_LINES];
static char header[100] = "\t\tEdit string by J.Willie. Copyright (C) 1994";
char *edit_string(char *buf, int create_mem)
{
char *p;
aborted = row = col = max_row = top_row = 0;
init_buffer();
string2buffer(buf);
/* Save current screen and setup edit screen */
ScreenRetrieve(store_screen);
ScreenGetCursor(&store_row, &store_col);
ScreenClear();
ScreenSetCursor(0, 0);
do_draw_header();
do_redraw_screen();
ScreenSetCursor(SCREEN(row), col);
handle_input();
/* Restore the old screen */
ScreenUpdate(store_screen);
ScreenSetCursor(store_row, store_col);
if (create_mem == 0)
{
buffer2string(buf);
p = buf;
}
else
{
int i, size;
for (i = 0, size = 0; i <= max_row; i++)
size += cols[i];
p = (char *)malloc(size+1);
buffer2string(p);
}
return ((aborted == 1) ? (char *)NULL : p);
}
/*
** This is the REAL guts of the program. Handles all the input and decides
** what should be done with it. The function is invisible to the program
** using edit_string()
*/
static void handle_input()
{
int c;
int i, j;
int start_word = -1;
int in_word = 0;
while(1)
{
show_info();
ScreenSetCursor(SCREEN(row), col);
c = getxkey();
if ((in_word == 0) && !isspace(c))
{
in_word = 1;
start_word = col;
}
else if (isspace(c))
{
start_word = -1;
in_word = 0;
}
switch (c)
{
case K_BackSpace:
case K_Delete:
if (col <= 0) /* Need to join lines */
{
if (row > START_ROW)
{
row--;
col = cols[row];
/* Copy start of next line to end of this one */
for (i = 0; (col <= 78); i++, col++)
{
buffer[row][col] = buffer[row+1][i];
if (buffer[row+1][i] == '\0')
break;
}
if (buffer[row+1][i] == '\0')
{
delete_line(row+1);
}
else
{
i--;
if (buffer[row][col-1] != '\n')
{
buffer[row][col] = '\n';
buffer[row][col+1] = '\0';
}
else
{
buffer[row][col] = '\0';
col--;
}
/* Copy Rest of the next line, to start of line */
for (j = 0; buffer[row+1][i] != '\0'; j++, i++)
{
buffer[row+1][j] = buffer[row+1][i];
}
cols[row+1] = j-1;
for (; j < 80; j++)
buffer[row+1][j] = '\0';
}
i = cols[row]; /* Remember where we want the cursor */
cols[row] = col; /* Set new line length */
col = i;
do_redraw_screen();
}
else
{
beep();
}
}
else
{
for (i = col; buffer[row][i] != '\0'; i++)
{
buffer[row][i-1] = buffer[row][i];
if (buffer[row][i] == '\n')
ScreenPutChar(' ', 7, i-1, SCREEN(row));
else
ScreenPutChar(buffer[row][i], 7, i-1, SCREEN(row));
}
ScreenPutChar(' ', 7, i, SCREEN(row));
buffer[row][i-1] = '\0';
col--;
cols[row] -= 1;
}
break;
case K_Tab:
case K_Escape:
case K_F2:
case K_F3:
case K_F4:
case K_F5:
case K_F6:
case K_F7:
case K_F8:
case K_F9:
case K_F10:
case K_F11:
case K_F12:
case K_EHome:
case K_EPageUp:
case K_EEnd:
case K_EPageDown:
case K_EInsert:
case K_EDelete:
break;
case K_EUp:
case K_Up:
if (row > START_ROW)
{
row--;
if (col > cols[row])
col = cols[row];
if (row < top_row)
{
top_row = row;
do_redraw_screen();
}
}
else
{
beep();
}
break;
case K_Left:
case K_Eleft:
if (col <= 0)
{
if (row > START_ROW)
{
row--;
col = cols[row];
}
else
{
beep();
}
}
else
{
col--;
}
break;
case K_Right:
case K_ERight:
if ((col >= 78) || (col >= cols[row]))
{
if (row == max_row)
{
}
else
{
col = 0;
row++;
}
}
else
col++;
break;
case K_Down:
case K_EDown:
if (row < max_row)
{
row++;
if (cols[row] < col)
col = cols[row];
if (row >= bot_row)
{
top_row++;
do_redraw_screen();
}
}
else
{
beep();
}
break;
case K_PageUp:
case K_Home:
case K_Center:
case K_End:
case K_PageDown:
case K_Insert:
case K_Shift_F2:
case K_Shift_F3:
case K_Shift_F4:
case K_Shift_F5:
case K_Shift_F6:
case K_Shift_F7:
case K_Shift_F8:
case K_Shift_F9:
case K_Shift_F10:
case K_Shift_F11:
case K_Shift_F12:
break;
/* Ignore Alt keys */
case K_Alt_KPMinus:
case K_Alt_KPPlus:
case K_Alt_KPStar:
case K_Alt_F2:
case K_Alt_F3:
case K_Alt_F4:
case K_Alt_F5:
case K_Alt_F6:
case K_Alt_F7:
case K_Alt_F8:
case K_Alt_F9:
case K_Alt_F10:
case K_Alt_F11:
case K_Alt_F12:
case K_Alt_1:
case K_Alt_2:
case K_Alt_3:
case K_Alt_4:
case K_Alt_5:
case K_Alt_6:
case K_Alt_7:
case K_Alt_8:
case K_Alt_9:
case K_Alt_0:
case K_Alt_Dash:
case K_Alt_Equals:
case K_Alt_EHome:
case K_Alt_EUp:
case K_Alt_EPageUp:
case K_Alt_ELeft:
case K_Alt_ERight:
case K_Alt_EEnd:
case K_Alt_EDown:
case K_Alt_EPageDown:
case K_Alt_EInsert:
case K_Alt_EDelete:
case K_Alt_KPSlash:
case K_Alt_Tab:
case K_Alt_Enter:
case K_Alt_Escape:
case K_Alt_Backspace:
case K_Alt_Q:
case K_Alt_W:
case K_Alt_E:
case K_Alt_R:
case K_Alt_T:
case K_Alt_Y:
case K_Alt_U:
case K_Alt_I:
case K_Alt_O:
case K_Alt_P:
case K_Alt_LBracket:
case K_Alt_RBracket:
case K_Alt_Return:
case K_Alt_A:
case K_Alt_S:
case K_Alt_D:
case K_Alt_F:
case K_Alt_G:
case K_Alt_H:
case K_Alt_J:
case K_Alt_K:
case K_Alt_L:
case K_Alt_Semicolon:
case K_Alt_Quote:
case K_Alt_Backquote:
case K_Alt_Backslash:
case K_Alt_Z:
case K_Alt_X:
case K_Alt_C:
case K_Alt_V:
case K_Alt_B:
case K_Alt_N:
case K_Alt_M:
case K_Alt_Comma:
case K_Alt_Period:
case K_Alt_Slash:
break;
case K_Control_X:
return;
break;
case K_Control_R:
do_redraw_screen();
break;
case K_Control_A:
#ifdef DEBUG
ScreenClear();
ScreenSetCursor(0,0);
for(i = START_ROW; i <= max_row; i++)
{
printf("%s%2d:%-3d\033[0m ",
(((i >= top_row) && (i<=bot_row)) ? "\033[35m" : ""),
i-START_ROW, cols[i]);
if ((i-START_ROW)%5 == 4)
printf("\n");
}
printf("\ntop_row = %d(%d), bot_row = %d(%d)\n",
top_row, (top_row-START_ROW), bot_row, (bot_row-START_ROW));
getxkey();
ScreenClear();
do_draw_header();
do_redraw_screen();
#endif
break;
/* Help keys */
case K_Control_F1:
case K_Alt_F1:
case K_Shift_F1:
case K_F1:
help_screen();
break;
/* ABORT the editing. We set the aborted variable and return NULL */
case K_Control_Q:
ScreenSetCursor(8, 6);
printf(" =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= \n");
ScreenSetCursor(9, 6);
printf(" | Are you sure you want to abort [Y]es/[N]o ? | \n");
ScreenSetCursor(10, 6);
printf(" =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= \n");
ScreenSetCursor(9, 58);
i = getxkey();
ScreenClear();
do_draw_header();
do_redraw_screen();
if ((i == 'Y') || (i == 'y'))
{
aborted = 1;
return;
}
break;
/* Lets just beep at control chars except control X */
case K_Control_ELeft:
case K_Control_ERight:
case K_Control_EEnd:
case K_Control_EPageDown:
case K_Control_EHome:
case K_Control_EPageUp:
case K_Control_EUp:
case K_Control_EDown:
case K_Control_EInsert:
case K_Control_EDelete:
case K_Control_B:
case K_Control_C:
case K_Control_D:
case K_Control_E:
case K_Control_F:
case K_Control_G:
case K_Control_K:
case K_Control_L:
case K_Control_N:
case K_Control_O:
case K_Control_P:
case K_Control_S:
case K_Control_T:
case K_Control_U:
case K_Control_V:
case K_Control_W:
case K_Control_Y:
case K_Control_Z:
case K_Control_BackSlash:
case K_Control_RBracket:
case K_Control_Caret:
case K_Control_Underscore:
case K_Control_At:
case K_Control_Backspace:
case K_Control_F2:
case K_Control_F3:
case K_Control_F4:
case K_Control_F5:
case K_Control_F6:
case K_Control_F7:
case K_Control_F8:
case K_Control_F9:
case K_Control_F10:
case K_Control_Left:
case K_Control_Right:
case K_Control_End:
case K_Control_PageDown:
case K_Control_Home:
case K_Control_PageUp:
case K_Control_F11:
case K_Control_F12:
case K_Control_Up:
case K_Control_KPDash:
case K_Control_Center:
case K_Control_KPPlus:
case K_Control_Down:
case K_Control_Insert:
case K_Control_Delete:
case K_Control_KPSlash:
case K_Control_KPStar:
break;
case K_LineFeed:
case K_Return:
if (col <= cols[row])
{
insert_line(row+1);
/* Copy end of line to start of new one */
for (i = col; buffer[row][i] != '\0'; i++)
{
buffer[row+1][i-col] = buffer[row][i];
buffer[row][i] = '\0';
}
buffer[row][col] = '\n';
cols[row] = col;
cols[row+1] = i-col;
}
else
{
buffer[row][col] = '\n';
buffer[row][col+1] = '\0';
cols[row] = col;
}
row++;
if (row >= bot_row)
{
top_row++;
}
if (row > max_row)
max_row = row;
col = 0;
do_redraw_screen();
break;
/* Lets display these ones and store them in the buffer */
case K_ExclamationPoint:
case K_DoubleQuote:
case K_Hash:
case K_Dollar:
case K_Percent:
case K_Ampersand:
case K_Quote:
case K_LParen:
case K_RParen:
case K_Star:
case K_Plua:
case K_Comma:
case K_Dash:
case K_Period:
case K_Slash:
case K_Colon:
case K_SemiColon:
case K_LAngle:
case K_Equals:
case K_RAngle:
case K_QuestionMark:
case K_At:
case K_LBracket:
case K_BackSlash:
case K_RBracket:
case K_Caret:
case K_UnderScore:
case K_BackQuote:
case K_LBrace:
case K_Pipe:
case K_RBrace:
case K_Tilde:
case K_Space:
default:
ScreenPutChar(c, 7, col, SCREEN(row));
/* Move the characters already on the line one space left */
if (col <= cols[row])
{
int i;
for (i = cols[row]; i >= col; i--)
{
buffer[row][i+1] = buffer[row][i];
if (buffer[row][i+1] != '\n')
ScreenPutChar(buffer[row][i+1], 7, i+1, SCREEN(row));
}
cols[row] += 1;
}
buffer[row][col] = (char)c;
col++;
if (col >= 78)
{
if ((in_word == 1) && (word_wrap != 0) && (start_word >= 20))
{
cols[row] = start_word;
insert_line(row+1);
if (row >= bot_row)
{
top_row++;
do_redraw_screen();
}
for (i = start_word; (i >= 0) && (i < col); i++)
{
buffer[row+1][i-start_word] = buffer[row][i];
ScreenPutChar(' ', 7, i, SCREEN(row));
ScreenPutChar((int)buffer[row][i], 7, i-start_word,SCREEN(row+1));
buffer[row][i] = ' ';
}
buffer[row][start_word] = '\n';
buffer[row][start_word+1] = '\0';
col = i - start_word;
start_word = -1;
cols[row+1] = col;
}
else
{
buffer[row][col] = '\n';
buffer[row][col+1] = '\0';
cols[row] = col-1;
col = 0;
}
row++;
if (row > max_row)
max_row = row;
if (row >= bot_row)
{
top_row++;
do_redraw_screen();
}
}
break;
}
}
}
static void do_redraw_screen()
{
int i, j;
ScreenSetCursor(START_ROW, 0);
for (i = top_row; (i <= bot_row); i++)
{
for (j = 0; j < 80; j++)
{
int y = START_ROW+i-top_row;
if ((cols[i] > j) && (buffer[i][j] != '\n'))
ScreenPutChar(buffer[i][j], 7, j, y);
else
ScreenPutChar(' ', 7, j, y);
}
}
ScreenSetCursor(SCREEN(row), col);
return;
}
static void do_draw_header()
{
ScreenSetCursor(0,0);
printf("%s\n", header);
printf("MOVE: Arrow keys. Finish: ^X Help:F1 Max Row: Row: "
"Col: \n"
"=-=-=-=-=-"
"=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"
);
show_info();
ScreenSetCursor(SCREEN(row), col);
return;
}
/*
** show_info() displays the max_row, row, col variables etc
*/
static void show_info()
{
int r,c;
ScreenGetCursor(&r, &c);
ScreenSetCursor(1, 54);
printf("%-2d", max_row-START_ROW);
fflush(stdout);
ScreenSetCursor(1, 61);
printf("%-2d", row-START_ROW);
fflush(stdout);
ScreenSetCursor(1, 68);
printf("%-2d", col);
fflush(stdout);
ScreenSetCursor(r, c);
}
void init_buffer()
{
int r;
for (r = 0; r < BUFFER_LINES; r++)
{
bzero((char *)buffer[r], LINELEN);
buffer[r][0] = '\0';
cols[r] = 0;
}
}
/*
** string2buffer takes the string to be edited and turns it into
** a buffer type.
*/
void string2buffer(char *s)
{
int r,c;
char *p;
int wd = -1;
for (p = s, r = START_ROW, c = 0; *p != '\0'; p++)
{
buffer[r][c] = *p;
if (*p == '\n')
{
cols[r] = c;
c = 0;
r++;
wd = -1;
}
else if (isspace(*p))
{
wd = -1;
c++;
}
else if ((wd == -1) && (isalnum(*p)))
{
wd = c;
c++;
}
else if (c >= 78)
{
if ((word_wrap != 0) && (wd > 20))
{
cols[r] = wd+1;
r++;
for (c = 0; c <= 78-wd; c++)
{
buffer[r][c] = buffer[r-1][wd+c];
buffer[r-1][wd+c] = '\0';
}
buffer[r-1][wd] = '\n';
}
else
{
buffer[r][c+1] = '\n';
buffer[r][c+2] = '\0';
cols[r] = c+1;
r++;
c = 0;
}
}
else
{
c++;
}
}
row = r;
max_row = r;
col = c;
cols[r] = c;
top_row = START_ROW;
}
/*
** buffer2string takes the edit buffer and turns it into a string
*/
static void buffer2string(char *s)
{
int r;
*s = '\0';
for (r = 0; r <= max_row; r++)
{
strcat(s, buffer[r]);
}
}
static void beep()
{
return;
}
static void delete_line(int r)
{
int x, y;
for (y = r; y <= max_row; y++)
{
for (x = 0; x <= 80; x++)
{
if (x <= cols[y+1])
buffer[y][x] = buffer[y+1][x];
else
buffer[y][x] = '\0';
}
cols[y] = cols[y+1];
}
for (x = 0; x < LINELEN; x++)
buffer[y][x] = '\0';
cols[y] = 0;
max_row--;
}
static void insert_line(int r)
{
int x, y;
for (y = max_row; y >= r; y--)
{
for (x = 0; x <= 80; x++)
{
if (x <= cols[y])
buffer[y+1][x] = buffer[y][x];
else
buffer[y+1][x] = '\0';
}
cols[y+1] = cols[y];
}
for (x = 0; x < LINELEN; x++)
buffer[r][x] = '\0';
cols[r] = 0;
max_row++;
}
void set_header(char *s)
{
int i;
if ((s == NULL) || (*s == '\0'))
{
header[0] = '\0';
}
else
{
for (i = 0; (i < 60) && (s[i] != '\n') && (s[i] != '\0'); i++)
header[i] = s[i];
header[i] = '\0';
}
}
static void help_screen()
{
ScreenClear();
ScreenSetCursor(0, 0);
printf(" Edit String by: James Willie. Copyright (C) 1994\n\n");
printf("^X <-- means press control and X together\n\n");
printf("Keys Function\n--------------------------\n");
printf("^X Quit and return string to caller (save)\n");
printf("^Q Abort editing.\n");
printf("^R Redraw the screen\n");
printf("F1 This help screen\n");
printf("Del/Backspace Delete the current character and move 1 space back\n");
printf("(Arrow keys) Move around the screen.\n");
printf("\n\nPress a key to return to editor ");
fflush(stdout);
getxkey();
ScreenClear();
do_draw_header();
do_redraw_screen();
return;
}
int abort_edit()
{
return ((aborted == 0) ? 0 : 1);
}