491 lines
9.6 KiB
C
491 lines
9.6 KiB
C
#include "text.h"
|
|
#include "text.xpm"
|
|
/**
|
|
* Word Processing Program
|
|
* Features:
|
|
* -Real time editing
|
|
* -Saving
|
|
* -Loading
|
|
**/
|
|
|
|
int winx, winy;
|
|
|
|
int y_offset = 0; // TODO: move to local scope
|
|
int tab_offset = 0;
|
|
extern void convert_xpm(char **xpm, unsigned char **buffer);
|
|
extern unsigned char *QUINN_Icon;
|
|
|
|
#define EDITOR_SIZE (WIN_SIZE - 2)
|
|
|
|
WINDOW *menu;
|
|
WINDOW *editor;
|
|
WINDOW *linenos;
|
|
|
|
short curs_color(int fg)
|
|
{
|
|
switch (7 & fg) { /* RGB */
|
|
case 0: /* 000 */
|
|
return (COLOR_BLACK);
|
|
case 1: /* 001 */
|
|
return (COLOR_BLUE);
|
|
case 2: /* 010 */
|
|
return (COLOR_GREEN);
|
|
case 3: /* 011 */
|
|
return (COLOR_CYAN);
|
|
case 4: /* 100 */
|
|
return (COLOR_RED);
|
|
case 5: /* 101 */
|
|
return (COLOR_MAGENTA);
|
|
case 6: /* 110 */
|
|
return (COLOR_YELLOW);
|
|
case 7: /* 111 */
|
|
return (COLOR_WHITE);
|
|
}
|
|
}
|
|
|
|
void init_colorpairs(void)
|
|
{
|
|
int fg, bg;
|
|
int colorpair;
|
|
|
|
for (bg = 0; bg <= 7; bg++) {
|
|
for (fg = 0; fg <= 7; fg++) {
|
|
colorpair = colornum(fg, bg);
|
|
init_pair(colorpair, curs_color(fg), curs_color(bg));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int colornum(int fg, int bg)
|
|
{
|
|
int B, bbb, ffff;
|
|
|
|
B = 1 << 7;
|
|
bbb = (7 & bg) << 4;
|
|
ffff = 7 & fg;
|
|
|
|
return (B | bbb | ffff);
|
|
}
|
|
|
|
|
|
void setcolor(int fg, int bg)
|
|
{
|
|
/* set the color pair (colornum) and bold/bright (A_BOLD) */
|
|
|
|
attron(COLOR_PAIR(colornum(fg, bg)));
|
|
if (is_bold(fg)) {
|
|
attron(A_BOLD);
|
|
}
|
|
}
|
|
|
|
void unsetcolor(int fg, int bg)
|
|
{
|
|
/* unset the color pair (colornum) and
|
|
bold/bright (A_BOLD) */
|
|
|
|
attroff(COLOR_PAIR(colornum(fg, bg)));
|
|
if (is_bold(fg)) {
|
|
attroff(A_BOLD);
|
|
}
|
|
}
|
|
|
|
int is_bold(int fg)
|
|
{
|
|
/* return the intensity bit */
|
|
|
|
int i;
|
|
|
|
i = 1 << 3;
|
|
return (i & fg);
|
|
}
|
|
|
|
|
|
void print_loc(int x, int y)
|
|
{
|
|
int oldx, oldy;
|
|
getyx(stdscr, oldy, oldx);
|
|
wmove(menu, 0, COLS - 20);
|
|
wprintw(menu, "X: %d Y: %d ", x, y + y_offset + 1);
|
|
wclrtoeol(menu);
|
|
move(oldy, oldx);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
PAGE page;
|
|
|
|
convert_xpm(text_xpm, &QUINN_Icon);
|
|
|
|
if(argc > 1)
|
|
{
|
|
if(file_exists(argv[1]))
|
|
{
|
|
load_file(&page, argv[1]);
|
|
}
|
|
else
|
|
{
|
|
init_page(&page, argv[1], PAGE_SIZE);
|
|
page.numlines = 1;
|
|
}
|
|
}
|
|
else // initialize
|
|
{
|
|
init_page(&page, "untitled.txt", PAGE_SIZE);
|
|
page.numlines = 1;
|
|
}
|
|
|
|
/* curses interface */
|
|
initscr();
|
|
noecho();
|
|
keypad(stdscr, 1);
|
|
|
|
start_color();
|
|
init_colorpairs();
|
|
|
|
winx = 0;
|
|
winy = 0;
|
|
|
|
menu = subwin(stdscr, 2, 80, 23, 0);
|
|
wbkgd(menu, COLOR_PAIR(colornum(COLOR_BLACK, COLOR_CYAN)));
|
|
|
|
editor = subwin(stdscr, 23, 75, 0, 5);
|
|
wbkgd(editor, COLOR_PAIR(colornum(COLOR_WHITE, COLOR_BLUE)));
|
|
|
|
linenos = subwin(stdscr, 23, 5, 0, 0);
|
|
wbkgd(linenos, COLOR_PAIR(colornum(COLOR_BLUE, COLOR_WHITE)));
|
|
|
|
int beg = 0;
|
|
int end = EDITOR_SIZE;
|
|
int i;
|
|
|
|
update_status(&page, "");
|
|
|
|
print_page(editor, linenos, &page, beg, end);
|
|
wmove(editor, winy, winx);
|
|
while(1)
|
|
{
|
|
|
|
beg = 0 + y_offset;
|
|
end = EDITOR_SIZE + y_offset;
|
|
int ch = wgetch(editor);
|
|
update_status(&page, ""); // default text
|
|
switch(ch)
|
|
{
|
|
case KEY_F(4):
|
|
if(prompt_yesno("Are you sure you want to quit?"))
|
|
goto endnc;
|
|
print_page(editor, linenos, &page, beg, end);
|
|
break;
|
|
case KEY_F(5):
|
|
save_file(&page);
|
|
update_status(&page, "Saved!");
|
|
break;
|
|
case KEY_F(6):
|
|
prompt_string("Save As:", page.filename, NAME_LIMIT);
|
|
save_file(&page);
|
|
print_page(editor, linenos, &page, beg, end);
|
|
update_status(&page, "Saved!");
|
|
break;
|
|
case KEY_UP:
|
|
move_up(&page);
|
|
break;
|
|
case KEY_DOWN:
|
|
move_down(&page);
|
|
break;
|
|
case KEY_LEFT:
|
|
move_left(&page);
|
|
break;
|
|
case KEY_RIGHT:
|
|
move_right(&page);
|
|
break;
|
|
case KEY_END:
|
|
move_to_eol(&page);
|
|
case KEY_DC:
|
|
case 127: // backspace key...
|
|
case KEY_BACKSPACE:
|
|
case '\b':
|
|
if(strlen(page.text[page.y].line) == 0)
|
|
{ // can only delete blank lines for now
|
|
remove_line(&page, page.y);
|
|
move_up(&page);
|
|
}
|
|
else if( page.x > 0 )
|
|
{
|
|
remove_char(&page.text[page.y], page.x - 1); // delete
|
|
move_left(&page); // char behind cursor
|
|
}
|
|
print_page(editor, linenos, &page, beg, end);
|
|
wmove(editor, winy, winx);
|
|
page.saved = 0;
|
|
break;
|
|
case '\t':
|
|
for(i = 0; i < TAB_WIDTH; i++)
|
|
{
|
|
insert_char(&page.text[page.y], ' ', page.x);
|
|
print_page(editor, linenos, &page, beg, end);
|
|
move_right(&page);
|
|
}
|
|
page.saved = 0;
|
|
break;
|
|
case '\n': // newline
|
|
insert_line(&page, page.y + 1);
|
|
print_page(editor, linenos, &page, beg, end);
|
|
move_down(&page);
|
|
break;
|
|
default: // all other chars
|
|
if( isprint(ch) )
|
|
{
|
|
insert_char(&page.text[page.y], ch, page.x);
|
|
print_page(editor, linenos, &page, beg, end);
|
|
move_right(&page);
|
|
page.saved = 0;
|
|
}
|
|
}
|
|
print_loc(page.x, page.y);
|
|
wrefresh(menu);
|
|
wrefresh(editor);
|
|
}
|
|
endnc:
|
|
/* end curses */
|
|
endwin();
|
|
dest_page(&page);
|
|
return EXIT_SUCCESS;
|
|
} // main
|
|
|
|
// prints a message at the bottom of the window
|
|
void update_status(PAGE *p, char *info)
|
|
{
|
|
char title[61];
|
|
|
|
snprintf(title, 60, "Text: %s%c", p->filename, p->saved == 1 ? ' ' : '*');
|
|
|
|
PDC_set_title(title);
|
|
wmove(menu, 0, 0);
|
|
wprintw(menu, "%s", info);
|
|
wmove(menu, 1, 0);
|
|
wprintw(menu, "F4: Quit F5: Save F6: Save As ");
|
|
wrefresh(menu);
|
|
} // update_status
|
|
|
|
/* movement */
|
|
|
|
void move_to_eol(PAGE *p)
|
|
{
|
|
while (p->x <= strlen(p->text[p->y].line)) {
|
|
move_right(p);
|
|
}
|
|
}
|
|
|
|
void move_left(PAGE *p)
|
|
{
|
|
if(p->x - 1 > 0) {
|
|
p->x--;
|
|
winx--;
|
|
winy = 0;
|
|
|
|
if (p->x > 75) {
|
|
winx = p->x % 75;
|
|
winy = p->x / 75;
|
|
} else {
|
|
winx = p->x;
|
|
}
|
|
|
|
for (int j = y_offset; j < p->y; j++) {
|
|
if (strlen(p->text[j].line) > 75) {
|
|
int linesinline = strlen(p->text[j].line) / 75;
|
|
if (strlen(p->text[j].line) % 75) linesinline++;
|
|
winy += linesinline;
|
|
} else {
|
|
winy++;
|
|
}
|
|
}
|
|
|
|
wmove(editor, winy, winx);
|
|
} else if (p->x - 1 == 0){
|
|
p->x = 0;
|
|
winx = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void move_right(PAGE *p)
|
|
{
|
|
if(p->x <= strlen(p->text[p->y].line))
|
|
{
|
|
p->x++;
|
|
winy = 0;
|
|
|
|
if (p->x > 75) {
|
|
winx = p->x % 75;
|
|
winy = p->x / 75;
|
|
} else {
|
|
winx++;
|
|
}
|
|
|
|
for (int j = y_offset; j < p->y; j++) {
|
|
if (strlen(p->text[j].line) > 75) {
|
|
int linesinline = strlen(p->text[j].line) / 75;
|
|
if (strlen(p->text[j].line) % 75) linesinline++;
|
|
winy += linesinline;
|
|
} else {
|
|
winy++;
|
|
}
|
|
}
|
|
|
|
wmove(editor, winy, winx);
|
|
}
|
|
}
|
|
|
|
void move_up(PAGE *p)
|
|
{
|
|
int linesinline = 1;
|
|
|
|
if (p->y > 0) {
|
|
if (strlen(p->text[p->y - 1].line) / 75 >= 1) {
|
|
linesinline = strlen(p->text[p->y - 1].line) / 75;
|
|
if (strlen(p->text[p->y - 1].line) % 75) linesinline++;
|
|
}
|
|
|
|
if( p->y - y_offset >= linesinline)
|
|
{
|
|
p->y--;
|
|
winy -= linesinline;
|
|
}
|
|
else if (y_offset > 0)
|
|
{
|
|
p->y--;
|
|
--(y_offset);
|
|
print_page(editor, linenos, p, 0 + y_offset, EDITOR_SIZE + y_offset);
|
|
} else {
|
|
p->y--;
|
|
winy -= linesinline;
|
|
}
|
|
if( p->x > strlen(p->text[p->y].line) ) { // cursor adjusts
|
|
p->x = strlen(p->text[p->y].line); // to smaller lines
|
|
winx = p->x;
|
|
}
|
|
|
|
wmove(editor, winy, winx);
|
|
}
|
|
}
|
|
void move_down(PAGE *p)
|
|
{
|
|
int linesinline = 1;
|
|
|
|
if (strlen(p->text[p->y].line) / 75 >= 1) {
|
|
linesinline = strlen(p->text[p->y].line) / 75;
|
|
if (strlen(p->text[p->y].line) % 75) linesinline++;
|
|
}
|
|
if( p->y - y_offset < EDITOR_SIZE - 1 && p->y -y_offset < p->numlines - 1 )
|
|
{
|
|
p->y++;
|
|
winy += linesinline;
|
|
}
|
|
else if ( p->y < p->numlines - 1 )
|
|
{
|
|
p->y++;
|
|
++(y_offset);
|
|
print_page(editor, linenos, p, 0 + y_offset, EDITOR_SIZE + y_offset);
|
|
}
|
|
|
|
if( p->x > strlen(p->text[p->y].line)) {
|
|
p->x = strlen(p->text[p->y].line);
|
|
winx = p->x;
|
|
}
|
|
wmove(editor, winy, winx);
|
|
|
|
}
|
|
/* movement */
|
|
|
|
int count_lines(FILE *fp)
|
|
{
|
|
char ch = '\0';
|
|
int count = 0;
|
|
while((ch = fgetc(fp)) != EOF)
|
|
if( ch == '\n' )
|
|
count++;
|
|
|
|
fseek(fp, 0, SEEK_SET); // go to beginning of file
|
|
return count;
|
|
} // count_lines
|
|
|
|
/* saving and loading */
|
|
void load_file(PAGE *p, char *filename)
|
|
{
|
|
FILE *fp = fopen(filename, "r");
|
|
int size = count_lines(fp) * 2;
|
|
char ch = '\0';
|
|
int line;
|
|
|
|
if(size < PAGE_SIZE)
|
|
size = PAGE_SIZE;
|
|
|
|
init_page(p, filename, size);
|
|
|
|
if(fp == NULL) // file doesn't exist yet. don't bother reading
|
|
{
|
|
p->numlines = 1;
|
|
return;
|
|
}
|
|
|
|
|
|
for(line = 0; line < size && ch != EOF; line++)
|
|
{
|
|
ch = fgetc(fp);
|
|
while(ch != '\n' && ch != EOF)
|
|
{
|
|
LINE *currline = &(p->text[line]);
|
|
if(ch != '\t')
|
|
{
|
|
add_char(currline, ch);
|
|
}
|
|
else // tab. add 4 spaces instead
|
|
{
|
|
int i;
|
|
for(i = 0; i < TAB_WIDTH; i++)
|
|
{
|
|
add_char(currline, ' ');
|
|
}
|
|
}
|
|
ch = fgetc(fp);
|
|
}
|
|
p->numlines++;
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
} // load_file
|
|
|
|
void save_file(PAGE *p)
|
|
{
|
|
FILE *fp = fopen(p->filename, "w");
|
|
int line, col;
|
|
|
|
for(line = 0; line < p->numlines; line++)
|
|
{
|
|
col = 0;
|
|
while(p->text[line].line[col] != '\0')
|
|
{
|
|
fputc(p->text[line].line[col], fp);
|
|
col++;
|
|
}
|
|
fputc('\n', fp);
|
|
}
|
|
|
|
fclose(fp);
|
|
p->saved = 1;
|
|
} // save_file
|
|
|
|
int file_exists(char *filename)
|
|
{
|
|
FILE *fp = fopen(filename, "r");
|
|
if(fp != NULL) {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
/* saving and loading */
|