nano/winio.c

1279 lines
30 KiB
C

/* $Id$ */
/**************************************************************************
* winio.c *
* *
* Copyright (C) 1999 Chris Allegretta *
* 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
* *
**************************************************************************/
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "config.h"
#include "proto.h"
#include "nano.h"
#ifndef NANO_SMALL
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif
static int statblank = 0; /* Number of keystrokes left after
we call statubar() before we
actually blank the statusbar */
/* Local Function Prototypes for only winio.c */
inline int get_page_from_virtual(int virtual);
inline int get_page_start_virtual(int page);
inline int get_page_end_virtual(int page);
/* Window I/O */
int do_first_line(void)
{
current = fileage;
placewewant = 0;
current_x = 0;
edit_update(current, CENTER);
return 1;
}
int do_last_line(void)
{
current = filebot;
placewewant = 0;
current_x = 0;
edit_update(current, CENTER);
return 1;
}
/* Like xplustabs, but for a specifc index of a speficific filestruct */
int xpt(filestruct * fileptr, int index)
{
int i, tabs = 0;
if (fileptr == NULL || fileptr->data == NULL)
return 0;
for (i = 0; i < index && fileptr->data[i] != 0; i++) {
tabs++;
if (fileptr->data[i] == NANO_CONTROL_I) {
if (tabs % tabsize == 0);
else
tabs += tabsize - (tabs % tabsize);
} else if (fileptr->data[i] & 0x80)
/* Make 8 bit chars only 1 collumn! */
;
else if (fileptr->data[i] < 32)
tabs++;
}
return tabs;
}
/* Return the actual place on the screen of current->data[current_x], which
should always be > current_x */
int xplustabs(void)
{
return xpt(current, current_x);
}
/* Return what current_x should be, given xplustabs() for the line,
* given a start position in the filestruct's data */
int actual_x_from_start(filestruct * fileptr, int xplus, int start)
{
int i, tot = 1;
if (fileptr == NULL || fileptr->data == NULL)
return 0;
for (i = start; tot <= xplus && fileptr->data[i] != 0; i++, tot++)
if (fileptr->data[i] == NANO_CONTROL_I) {
if (tot % tabsize == 0)
tot++;
else
tot += tabsize - (tot % tabsize);
} else if (fileptr->data[i] & 0x80)
tot++; /* Make 8 bit chars only 1 column (again) */
else if (fileptr->data[i] < 32)
tot += 2;
#ifdef DEBUG
fprintf(stderr, _("actual_x_from_start for xplus=%d returned %d\n"),
xplus, i);
#endif
return i - start;
}
/* Opposite of xplustabs */
inline int actual_x(filestruct * fileptr, int xplus)
{
return actual_x_from_start(fileptr, xplus, 0);
}
/* a strlen with tabs factored in, similar to xplustabs() */
int strlenpt(char *buf)
{
int i, tabs = 0;
if (buf == NULL)
return 0;
for (i = 0; buf[i] != 0; i++) {
tabs++;
if (buf[i] == NANO_CONTROL_I) {
if (tabs % tabsize == 0);
else
tabs += tabsize - (tabs % tabsize);
} else if (buf[i] & 0x80)
/* Make 8 bit chars only 1 collumn! */
;
else if (buf[i] < 32)
tabs++;
}
return tabs;
}
/* resets current_y based on the position of current and puts the cursor at
(current_y, current_x) */
void reset_cursor(void)
{
filestruct *ptr = edittop;
int x;
current_y = 0;
while (ptr != current && ptr != editbot && ptr->next != NULL) {
ptr = ptr->next;
current_y++;
}
x = xplustabs();
if (x <= COLS - 2)
wmove(edit, current_y, x);
else
wmove(edit, current_y, x -
get_page_start_virtual(get_page_from_virtual(x)));
}
void blank_bottombars(void)
{
int i = no_help()? 3 : 1;
for (; i <= 2; i++)
mvwaddstr(bottomwin, i, 0, hblank);
}
void blank_edit(void)
{
int i;
for (i = 0; i <= editwinrows - 1; i++)
mvwaddstr(edit, i, 0, hblank);
wrefresh(edit);
}
void blank_statusbar(void)
{
mvwaddstr(bottomwin, 0, 0, hblank);
}
void blank_statusbar_refresh(void)
{
blank_statusbar();
wrefresh(bottomwin);
}
void check_statblank(void)
{
if (statblank > 1)
statblank--;
else if (statblank == 1 && !ISSET(CONSTUPDATE)) {
statblank--;
blank_statusbar_refresh();
}
}
/* Repaint the statusbar when getting a character in nanogetstr */
void nanoget_repaint(char *buf, char *inputbuf, int x)
{
blank_statusbar();
mvwaddstr(bottomwin, 0, 0, buf);
waddstr(bottomwin, inputbuf);
wmove(bottomwin, 0, x);
}
/* Get the input from the kb, this should only be called from statusq */
int nanogetstr(char *buf, char *def, shortcut s[], int slen, int start_x)
{
int kbinput = 0, j = 0, x = 0, xend;
int x_left = 0;
char inputstr[132], inputbuf[132] = "";
blank_statusbar();
mvwaddstr(bottomwin, 0, 0, buf);
if (strlen(def) > 0)
waddstr(bottomwin, def);
wrefresh(bottomwin);
x_left = strlen(buf);
x = strlen(def) + x_left;
/* Get the input! */
if (strlen(def) > 0) {
strcpy(answer, def);
strcpy(inputbuf, def);
}
while ((kbinput = wgetch(bottomwin)) != 13) {
for (j = 0; j <= slen - 1; j++) {
if (kbinput == s[j].val) {
strcpy(answer, "");
return s[j].val;
}
}
xend = strlen(buf) + strlen(inputbuf);
switch (kbinput) {
/* Stuff we want to equate with <enter>, ASCII 13 */
case 343:
ungetch(13); /* Enter on iris-ansi $TERM, sometimes */
break;
case KEY_HOME:
x = x_left;
nanoget_repaint(buf, inputbuf, x);
break;
case KEY_END:
x = x_left + strlen(inputbuf);
nanoget_repaint(buf, inputbuf, x);
break;
case KEY_RIGHT:
if (x < xend)
x++;
wmove(bottomwin, 0, x);
break;
case NANO_CONTROL_D:
if (strlen(inputbuf) > 0 && (x - x_left) != strlen(inputbuf)) {
memmove(inputbuf + (x - x_left),
inputbuf + (x - x_left) + 1,
strlen(inputbuf) - (x - x_left) - 1);
inputbuf[strlen(inputbuf) - 1] = 0;
}
nanoget_repaint(buf, inputbuf, x);
break;
case NANO_CONTROL_K:
case NANO_CONTROL_U:
*inputbuf = 0;
x = x_left;
nanoget_repaint(buf, inputbuf, x);
break;
case KEY_BACKSPACE:
case KEY_DC:
case 127:
case NANO_CONTROL_H:
if (strlen(inputbuf) > 0) {
if (x == (x_left + strlen(inputbuf)))
inputbuf[strlen(inputbuf) - 1] = 0;
else if (x - x_left) {
memmove(inputbuf + (x - x_left) - 1,
inputbuf + (x - x_left),
strlen(inputbuf) - (x - x_left));
inputbuf[strlen(inputbuf) - 1] = 0;
}
}
blank_statusbar();
mvwaddstr(bottomwin, 0, 0, buf);
waddstr(bottomwin, inputbuf);
case KEY_LEFT:
if (x > strlen(buf))
x--;
wmove(bottomwin, 0, x);
break;
case KEY_UP:
case KEY_DOWN:
break;
case 27:
switch (kbinput = wgetch(edit)) {
case 79:
switch (kbinput = wgetch(edit)) {
case 70:
x = x_left + strlen(inputbuf);
nanoget_repaint(buf, inputbuf, x);
break;
case 72:
x = x_left;
nanoget_repaint(buf, inputbuf, x);
break;
}
break;
case 91:
switch (kbinput = wgetch(edit)) {
case 'C':
if (x < xend)
x++;
wmove(bottomwin, 0, x);
break;
case 'D':
if (x > strlen(buf))
x--;
wmove(bottomwin, 0, x);
break;
case 49:
x = x_left;
nanoget_repaint(buf, inputbuf, x);
goto skip_126;
case 51:
if (strlen(inputbuf) > 0
&& (x - x_left) != strlen(inputbuf)) {
memmove(inputbuf + (x - x_left),
inputbuf + (x - x_left) + 1,
strlen(inputbuf) - (x - x_left) - 1);
inputbuf[strlen(inputbuf) - 1] = 0;
}
nanoget_repaint(buf, inputbuf, x);
goto skip_126;
case 52:
x = x_left + strlen(inputbuf);
nanoget_repaint(buf, inputbuf, x);
goto skip_126;
skip_126:
nodelay(edit, TRUE);
kbinput = wgetch(edit);
if (kbinput == 126 || kbinput == ERR)
kbinput = -1;
nodelay(edit, FALSE);
break;
}
}
nanoget_repaint(buf, inputbuf, x);
break;
default:
if (kbinput < 32)
break;
strcpy(inputstr, inputbuf);
inputstr[x - strlen(buf)] = kbinput;
strcpy(&inputstr[x - strlen(buf) + 1],
&inputbuf[x - strlen(buf)]);
strcpy(inputbuf, inputstr);
x++;
nanoget_repaint(buf, inputbuf, x);
#ifdef DEBUG
fprintf(stderr, _("input \'%c\' (%d)\n"), kbinput, kbinput);
#endif
}
wrefresh(bottomwin);
}
strncpy(answer, inputbuf, 132);
/* Now that the text is editable instead of bracketed, we have to
check for answer == def, instead of answer == "" */
/* if (!strcmp(answer, "")) */
if (!strcmp(answer, def))
return -2;
else
return 0;
}
void horizbar(WINDOW * win, int y)
{
wattron(win, A_REVERSE);
mvwaddstr(win, 0, 0, hblank);
wattroff(win, A_REVERSE);
}
void titlebar(void)
{
int namelen, space;
horizbar(topwin, 0);
wattron(topwin, A_REVERSE);
mvwaddstr(topwin, 0, 3, VERMSG);
space = COLS - strlen(VERMSG) - strlen(VERSION) - 21;
namelen = strlen(filename);
if (!strcmp(filename, ""))
mvwaddstr(topwin, 0, center_x - 6, _("New Buffer"));
else {
if (namelen > space) {
waddstr(topwin, _(" File: ..."));
waddstr(topwin, &filename[namelen - space]);
} else {
mvwaddstr(topwin, 0, center_x - (namelen / 2 + 1), "File: ");
waddstr(topwin, filename);
}
}
if (ISSET(MODIFIED))
mvwaddstr(topwin, 0, COLS - 10, _("Modified"));
wattroff(topwin, A_REVERSE);
wrefresh(topwin);
reset_cursor();
}
void onekey(char *keystroke, char *desc)
{
char description[80];
snprintf(description, 12, " %-10s", desc);
wattron(bottomwin, A_REVERSE);
waddstr(bottomwin, keystroke);
wattroff(bottomwin, A_REVERSE);
waddstr(bottomwin, description);
}
void clear_bottomwin(void)
{
if (ISSET(NO_HELP))
return;
mvwaddstr(bottomwin, 1, 0, hblank);
mvwaddstr(bottomwin, 2, 0, hblank);
}
void bottombars(shortcut s[], int slen)
{
int i, j, k;
char keystr[10];
if (ISSET(NO_HELP))
return;
/* Determine how many extra spaces are needed to fill the bottom of the screen */
k = COLS / 6 - 13;
clear_bottomwin();
wmove(bottomwin, 1, 0);
for (i = 0; i <= slen - 1; i += 2) {
snprintf(keystr, 10, "^%c", s[i].val + 64);
onekey(keystr, s[i].desc);
for (j = 0; j < k; j++)
waddch(bottomwin, ' ');
}
wmove(bottomwin, 2, 0);
for (i = 1; i <= slen - 1; i += 2) {
snprintf(keystr, 10, "^%c", s[i].val + 64);
onekey(keystr, s[i].desc);
for (j = 0; j < k; j++)
waddch(bottomwin, ' ');
}
wrefresh(bottomwin);
}
/* If modified is not already set, set it and update titlebar */
void set_modified(void)
{
if (!ISSET(MODIFIED)) {
SET(MODIFIED);
titlebar();
wrefresh(topwin);
}
}
/* And so start the display update routines */
/* Given a column, this returns the "page" it is on */
/* "page" in the case of the display columns, means which set of 80 */
/* characters is viewable (ie: page 1 shows from 1 to COLS) */
inline int get_page_from_virtual(int virtual)
{
int page = 2;
if (virtual <= COLS - 2)
return 1;
virtual -= (COLS - 2);
while (virtual > COLS - 2 - 7) {
virtual -= (COLS - 2 - 7);
page++;
}
return page;
}
/* The inverse of the above function */
inline int get_page_start_virtual(int page)
{
int virtual;
virtual = --page * (COLS - 7);
if (page)
virtual -= 2 * page - 1;
return virtual;
}
inline int get_page_end_virtual(int page)
{
return get_page_start_virtual(page) + COLS - 1;
}
#ifndef NANO_SMALL
/* This takes care of the case where there is a mark that covers only */
/* the current line. */
/* It expects a line with no tab characers (ie: the type that edit_add */
/* deals with */
void add_marked_sameline(int begin, int end, filestruct * fileptr, int y,
int virt_cur_x, int this_page)
{
/*
* The general idea is to break the line up into 3 sections: before
* the mark, the mark, and after the mark. We then paint each in
* turn (for those that are currently visible, of course
*
* 3 start points: 0 -> begin, begin->end, end->strlen(data)
* in data : pre sel post
*/
int this_page_start = get_page_start_virtual(this_page),
this_page_end = get_page_end_virtual(this_page);
/* likewise, 3 data lengths */
int pre_data_len = begin, sel_data_len = end - begin, post_data_len = 0; /* Determined from the other two */
/* now fix the start locations & lengths according to the cursor's
* position (ie: our page) */
if (pre_data_len < this_page_start)
pre_data_len = 0;
else
pre_data_len -= this_page_start;
if (begin < this_page_start)
begin = this_page_start;
if (end < this_page_start)
end = this_page_start;
if (begin > this_page_end)
begin = this_page_end;
if (end > this_page_end)
end = this_page_end;
/* Now calculate the lengths */
sel_data_len = end - begin;
post_data_len = this_page_end - end;
/* Paint this line! */
mvwaddnstr(edit, y, 0, &fileptr->data[this_page_start], pre_data_len);
wattron(edit, A_REVERSE);
mvwaddnstr(edit, y, begin - this_page_start,
&fileptr->data[begin], sel_data_len);
wattroff(edit, A_REVERSE);
mvwaddnstr(edit, y, end - this_page_start,
&fileptr->data[end], post_data_len);
}
#endif
/* edit_add takes care of the job of actually painting a line into the
* edit window.
*
* Called only from update_line. Expects a converted-to-not-have-tabs
* line */
void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x,
int virt_mark_beginx, int this_page)
{
#ifndef NANO_SMALL
/* There are quite a few cases that could take place, we'll deal
* with them each in turn */
if (ISSET(MARK_ISSET)
&& !((fileptr->lineno > mark_beginbuf->lineno
&& fileptr->lineno > current->lineno)
|| (fileptr->lineno < mark_beginbuf->lineno
&& fileptr->lineno < current->lineno))) {
/* If we get here we are on a line that is atleast
* partially selected. The lineno checks above determined
* that */
if (fileptr != mark_beginbuf && fileptr != current) {
/* We are on a completely marked line, paint it all
* inverse */
wattron(edit, A_REVERSE);
mvwaddnstr(edit, yval, 0, fileptr->data, COLS);
wattroff(edit, A_REVERSE);
} else if (fileptr == mark_beginbuf && fileptr == current) {
/* Special case, we're still on the same line we started
* marking -- so we call our helper function */
if (virt_cur_x < virt_mark_beginx) {
/* To the right of us is marked */
add_marked_sameline(virt_cur_x, virt_mark_beginx,
fileptr, yval, virt_cur_x, this_page);
} else {
/* To the left of us is marked */
add_marked_sameline(virt_mark_beginx, virt_cur_x,
fileptr, yval, virt_cur_x, this_page);
}
} else if (fileptr == mark_beginbuf) {
/*
* we're updating the line that was first marked
* but we're not currently on it. So we want to
* figur out which half to invert based on our
* relative line numbers.
*
* i.e. If we're above the "beginbuf" line, we want to
* mark the left side. Otherwise we're below, so we
* mark the right
*/
int target;
if (mark_beginbuf->lineno > current->lineno)
wattron(edit, A_REVERSE);
target =
(virt_mark_beginx <
COLS - 1) ? virt_mark_beginx : COLS - 1;
mvwaddnstr(edit, yval, 0, fileptr->data, target);
if (mark_beginbuf->lineno < current->lineno)
wattron(edit, A_REVERSE);
else
wattroff(edit, A_REVERSE);
target = (COLS - 1) - virt_mark_beginx;
if (target < 0)
target = 0;
mvwaddnstr(edit, yval, virt_mark_beginx,
&fileptr->data[virt_mark_beginx], target);
if (mark_beginbuf->lineno < current->lineno)
wattroff(edit, A_REVERSE);
} else if (fileptr == current) {
/* we're on the cursors line, but it's not the first
* one we marked. Similar to the previous logic. */
int this_page_start = get_page_start_virtual(this_page),
this_page_end = get_page_end_virtual(this_page);
if (mark_beginbuf->lineno < current->lineno)
wattron(edit, A_REVERSE);
if (virt_cur_x > COLS - 2) {
mvwaddnstr(edit, yval, 0,
&fileptr->data[this_page_start],
virt_cur_x - this_page_start);
} else {
mvwaddnstr(edit, yval, 0, fileptr->data, virt_cur_x);
}
if (mark_beginbuf->lineno > current->lineno)
wattron(edit, A_REVERSE);
else
wattroff(edit, A_REVERSE);
if (virt_cur_x > COLS - 2)
mvwaddnstr(edit, yval, virt_cur_x - this_page_start,
&fileptr->data[virt_cur_x],
this_page_end - virt_cur_x);
else
mvwaddnstr(edit, yval, virt_cur_x,
&fileptr->data[virt_cur_x], COLS - virt_cur_x);
if (mark_beginbuf->lineno > current->lineno)
wattroff(edit, A_REVERSE);
}
} else
#endif
/* Just paint the string (no mark on this line) */
mvwaddnstr(edit, yval, 0, &fileptr->data[start],
get_page_end_virtual(this_page) - start);
}
/*
* Just update one line in the edit buffer. Basically a wrapper for
* edit_add
*
* index gives is a place in the string to update starting from.
* Likely args are current_x or 0.
*/
void update_line(filestruct * fileptr, int index)
{
filestruct *filetmp;
int line = 0, col = 0;
int virt_cur_x = current_x, virt_mark_beginx = mark_beginx;
char *realdata, *tmp;
int i, pos, len, page;
if (!fileptr)
return;
/* First, blank out the line (at a minimum) */
for (filetmp = edittop; filetmp != fileptr && filetmp != editbot;
filetmp = filetmp->next)
line++;
mvwaddstr(edit, line, 0, hblank);
/* Next, convert all the tabs to spaces so everything else is easy */
index = xpt(fileptr, index);
realdata = fileptr->data;
len = strlen(realdata);
fileptr->data = nmalloc(xpt(fileptr, len) + 1);
pos = 0;
for (i = 0; i < len; i++) {
if (realdata[i] == '\t') {
do {
fileptr->data[pos++] = ' ';
if (i < current_x)
virt_cur_x++;
if (i < mark_beginx)
virt_mark_beginx++;
} while (pos % tabsize);
/* must decrement once to account for tab-is-one-character */
if (i < current_x)
virt_cur_x--;
if (i < mark_beginx)
virt_mark_beginx--;
} else if (realdata[i] >= 1 && realdata[i] <= 26) {
/* Treat control characters as ^letter */
fileptr->data[pos++] = '^';
fileptr->data[pos++] = realdata[i] + 64;
} else {
fileptr->data[pos++] = realdata[i];
}
}
fileptr->data[pos] = '\0';
/* Now, Paint the line */
if (current == fileptr && index > COLS - 2) {
/* This handles when the current line is beyond COLS */
/* It requires figureing out what page we're at */
page = get_page_from_virtual(index);
col = get_page_start_virtual(page);
edit_add(filetmp, line, col, virt_cur_x, virt_mark_beginx, page);
mvwaddch(edit, line, 0, '$');
if (strlenpt(fileptr->data) > get_page_end_virtual(page))
mvwaddch(edit, line, COLS - 1, '$');
} else {
/* It's not the current line means that it's at x=0 and page=1 */
/* If it is the current line, then we're in the same boat */
edit_add(filetmp, line, 0, virt_cur_x, virt_mark_beginx, 1);
if (strlenpt(&filetmp->data[col]) > COLS)
mvwaddch(edit, line, COLS - 1, '$');
}
/* Clean up our mess */
tmp = fileptr->data;
fileptr->data = realdata;
free(tmp);
}
void center_cursor(void)
{
current_y = editwinrows / 2;
wmove(edit, current_y, current_x);
}
/* Refresh the screen without changing the position of lines */
void edit_refresh(void)
{
static int noloop = 0;
int lines = 0, i = 0, currentcheck = 0;
filestruct *temp, *hold = current;
if (current == NULL)
return;
temp = edittop;
while (lines <= editwinrows - 1 && lines <= totlines && temp != NULL) {
hold = temp;
update_line(temp, current_x);
if (temp == current)
currentcheck = 1;
temp = temp->next;
lines++;
}
/* If noloop == 1, then we already did an edit_update without finishing
this function. So we don't run edit_update again */
if (!currentcheck && !noloop) { /* Then current has run off the screen... */
edit_update(current, CENTER);
noloop = 1;
} else if (noloop)
noloop = 0;
if (lines <= editwinrows - 1)
while (lines <= editwinrows - 1) {
mvwaddstr(edit, lines, i, hblank);
lines++;
}
if (temp == NULL)
editbot = hold;
else
editbot = temp;
}
/*
* Same as above, but touch the window first so everything is redrawn.
*/
void edit_refresh_clearok(void)
{
clearok(edit, TRUE);
edit_refresh();
clearok(edit, FALSE);
}
/*
* Nice generic routine to update the edit buffer given a pointer to the
* file struct =)
*/
void edit_update(filestruct * fileptr, int topmidbot)
{
int i = 0;
filestruct *temp;
if (fileptr == NULL)
return;
temp = fileptr;
if (topmidbot == 2);
else if (topmidbot == 0)
for (i = 0; i <= editwinrows - 1 && temp->prev != NULL; i++)
temp = temp->prev;
else
for (i = 0; i <= editwinrows / 2 && temp->prev != NULL; i++)
temp = temp->prev;
edittop = temp;
fix_editbot();
edit_refresh();
}
/* This function updates current based on where current_y is, reset_cursor
does the opposite */
void update_cursor(void)
{
int i = 0;
#ifdef DEBUG
fprintf(stderr, _("Moved to (%d, %d) in edit buffer\n"), current_y,
current_x);
#endif
current = edittop;
while (i <= current_y - 1 && current->next != NULL) {
current = current->next;
i++;
}
#ifdef DEBUG
fprintf(stderr, _("current->data = \"%s\"\n"), current->data);
#endif
}
/*
* Ask a question on the statusbar. Answer will be stored in answer
* global. Returns -1 on aborted enter, -2 on a blank string, and 0
* otherwise, the valid shortcut key caught, Def is any editable text we
* want to put up by default.
*/
int statusq(shortcut s[], int slen, char *def, char *msg, ...)
{
va_list ap;
char foo[133];
int ret;
bottombars(s, slen);
va_start(ap, msg);
vsnprintf(foo, 132, msg, ap);
va_end(ap);
strncat(foo, ": ", 132);
wattron(bottomwin, A_REVERSE);
ret = nanogetstr(foo, def, s, slen, (strlen(foo) + 3));
wattroff(bottomwin, A_REVERSE);
switch (ret) {
case NANO_FIRSTLINE_KEY:
do_first_line();
break;
case NANO_LASTLINE_KEY:
do_last_line();
break;
case NANO_CANCEL_KEY:
return -1;
default:
blank_statusbar_refresh();
}
#ifdef DEBUG
fprintf(stderr, _("I got \"%s\"\n"), answer);
#endif
return ret;
}
/*
* Ask a simple yes/no question on the statusbar. Returns 1 for Y, 0 for
* N, 2 for All (if all is non-zero when passed in) and -1 for abort (^C)
*/
int do_yesno(int all, int leavecursor, char *msg, ...)
{
va_list ap;
char foo[133];
int kbinput, ok = -1;
/* Write the bottom of the screen */
clear_bottomwin();
wattron(bottomwin, A_REVERSE);
blank_statusbar_refresh();
wattroff(bottomwin, A_REVERSE);
/* Remove gettext call for keybindings until we clear the thing up */
if (!ISSET(NO_HELP)) {
wmove(bottomwin, 1, 0);
onekey(" Y", _("Yes"));
if (all)
onekey(" A", _("All"));
wmove(bottomwin, 2, 0);
onekey(" N", _("No"));
onekey("^C", _("Cancel"));
}
va_start(ap, msg);
vsnprintf(foo, 132, msg, ap);
va_end(ap);
wattron(bottomwin, A_REVERSE);
mvwaddstr(bottomwin, 0, 0, foo);
wattroff(bottomwin, A_REVERSE);
wrefresh(bottomwin);
if (leavecursor == 1)
reset_cursor();
while (ok == -1) {
kbinput = wgetch(edit);
switch (kbinput) {
case 'Y':
case 'y':
ok = 1;
break;
case 'N':
case 'n':
ok = 0;
break;
case 'A':
case 'a':
if (all)
ok = 2;
break;
case NANO_CONTROL_C:
ok = -2;
break;
}
}
/* Then blank the screen */
blank_statusbar_refresh();
if (ok == -2)
return -1;
else
return ok;
}
void statusbar(char *msg, ...)
{
va_list ap;
char foo[133];
int start_x = 0;
va_start(ap, msg);
vsnprintf(foo, 132, msg, ap);
va_end(ap);
start_x = center_x - strlen(foo) / 2 - 1;
/* Blank out line */
blank_statusbar();
wmove(bottomwin, 0, start_x);
wattron(bottomwin, A_REVERSE);
waddstr(bottomwin, "[ ");
waddstr(bottomwin, foo);
waddstr(bottomwin, " ]");
wattroff(bottomwin, A_REVERSE);
wrefresh(bottomwin);
if (ISSET(CONSTUPDATE))
statblank = 1;
else
statblank = 25;
}
void display_main_list(void)
{
bottombars(main_list, MAIN_VISIBLE);
}
int total_refresh(void)
{
clearok(edit, TRUE);
clearok(topwin, TRUE);
clearok(bottomwin, TRUE);
wnoutrefresh(edit);
wnoutrefresh(topwin);
wnoutrefresh(bottomwin);
doupdate();
clearok(edit, FALSE);
clearok(topwin, FALSE);
clearok(bottomwin, FALSE);
edit_refresh();
titlebar();
return 1;
}
void previous_line(void)
{
if (current_y > 0)
current_y--;
}
int do_cursorpos(void)
{
filestruct *fileptr;
float linepct, bytepct;
int i, tot = 0;
if (current == NULL || fileage == NULL)
return 0;
for (fileptr = fileage; fileptr != current && fileptr != NULL;
fileptr = fileptr->next)
tot += strlen(fileptr->data) + 1;
if (fileptr == NULL)
return -1;
i = tot + current_x;;
for (fileptr = current->next; fileptr != NULL; fileptr = fileptr->next)
tot += strlen(fileptr->data) + 1;
if (totlines > 0)
linepct = 100 * current->lineno / totlines;
else
linepct = 0;
if (totsize > 0)
bytepct = 100 * i / totsize;
else
bytepct = 0;
#ifdef DEBUG
fprintf(stderr, _("do_cursorpos: linepct = %f, bytepct = %f\n"),
linepct, bytepct);
#endif
statusbar(_("line %d of %d (%.0f%%), character %d of %d (%.0f%%)"),
current->lineno, totlines, linepct, i, totsize, bytepct);
reset_cursor();
return 1;
}
/* Our broken, non-shortcut list compliant help function.
But hey, it's better than nothing, and it's dynamic! */
int do_help(void)
{
#ifndef NANO_SMALL
char *ptr = help_text, *end;
int i, j, row = 0, page = 1, kbinput = 0, no_more = 0;
int no_help_flag = 0;
blank_edit();
curs_set(0);
blank_statusbar();
if (ISSET(NO_HELP)) {
no_help_flag = 1;
delwin(bottomwin);
bottomwin = newwin(3, COLS, LINES - 3, 0);
keypad(bottomwin, TRUE);
editwinrows -= no_help();
UNSET(NO_HELP);
bottombars(help_list, HELP_LIST_LEN);
} else
bottombars(help_list, HELP_LIST_LEN);
do {
ptr = help_text;
switch (kbinput) {
case NANO_NEXTPAGE_KEY:
case NANO_NEXTPAGE_FKEY:
case KEY_NPAGE:
if (!no_more) {
blank_edit();
page++;
}
break;
case NANO_PREVPAGE_KEY:
case NANO_PREVPAGE_FKEY:
case KEY_PPAGE:
if (page > 1) {
no_more = 0;
blank_edit();
page--;
}
break;
}
/* Calculate where in the text we should be based on the page */
for (i = 1; i < page; i++) {
row = 0;
j = 0;
while (row < editwinrows - 2 && *ptr != '\0') {
if (*ptr == '\n' || j == COLS - 5) {
j = 0;
row++;
}
ptr++;
j++;
}
}
if (i > 1) {
}
i = 0;
j = 0;
while (i < editwinrows && *ptr != '\0') {
end = ptr;
while (*end != '\n' && *end != '\0' && j != COLS - 5) {
end++;
j++;
}
if (j == COLS - 5) {
/* Don't print half a word if we've run of of space */
while (*end != ' ' && *end != '\0') {
end--;
j--;
}
}
mvwaddnstr(edit, i, 0, ptr, j);
j = 0;
i++;
if (*end == '\n')
end++;
ptr = end;
}
if (*ptr == '\0') {
no_more = 1;
continue;
}
} while ((kbinput = wgetch(edit)) != NANO_EXIT_KEY);
if (no_help_flag) {
werase(bottomwin);
wrefresh(bottomwin);
delwin(bottomwin);
SET(NO_HELP);
bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
keypad(bottomwin, TRUE);
editwinrows += no_help();
} else
display_main_list();
curs_set(1);
edit_refresh();
#else
nano_small_msg();
#endif
return 1;
}
/* Dump the current file structure to stderr */
void dump_buffer(filestruct * inptr)
{
#ifdef DEBUG
filestruct *fileptr;
if (inptr == fileage)
fprintf(stderr, _("Dumping file buffer to stderr...\n"));
else if (inptr == cutbuffer)
fprintf(stderr, _("Dumping cutbuffer to stderr...\n"));
else
fprintf(stderr, _("Dumping a buffer to stderr...\n"));
fileptr = inptr;
while (fileptr != NULL) {
fprintf(stderr, "(%ld) %s\n", fileptr->lineno, fileptr->data);
fflush(stderr);
fileptr = fileptr->next;
}
#endif /* DEBUG */
}
void dump_buffer_reverse(filestruct * inptr)
{
#ifdef DEBUG
filestruct *fileptr;
fileptr = filebot;
while (fileptr != NULL) {
fprintf(stderr, "(%ld) %s\n", fileptr->lineno, fileptr->data);
fflush(stderr);
fileptr = fileptr->prev;
}
#endif /* DEBUG */
}
/* Fix editbot based on the assumption that edittop is correct */
void fix_editbot(void)
{
int i;
editbot = edittop;
for (i = 0; (i <= editwinrows - 1) && (editbot->next != NULL)
&& (editbot != filebot); i++, editbot = editbot->next);
}