/*
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, see <http://www.gnu.org/licenses/>.
Copyright 2013 inkblack.
*/
#include <ncurses.h>
#include <string.h>
/*
Первая модель текстового редактора.
1. Подразумеваем, что терминал имеет размер 24x80.
ЕСЛИ ЗАПУСТИТЬ В МЕНЬШЕМ ТЕРМИНАЛЕ, БУДЕТ РАБОТАТЬ НЕКОРРЕКТНО!
2. Пока вместо файла в окно редактирования закидываются строки вида:
1 __________ 1 __________ 1 __________ 1 __________
2 __________ 2 __________ 2 __________ 2 __________
...
21 __________ 21 __________ 21 __________ 21 __________
22 __________ 22 __________ 22 __________ 22 __________
...
Ну, или что-то подобное.
3. Ниже задается количество строк в "файле" и максимальное количество
символов в строке.
*/
#define MAX_ROWS 30
#define MAX_COLS 181
/// Размер окна, в котором будем показывать наш файл.
#define V_ROWS 23
#define V_COLS 80
/// Тут же формат строки статуса, поскольку ее длина должна равняться V_COLS ;
/// если V_COLS поменяется, то STAT_FMT тоже надо будет изменить.
const char* STAT_FMT = "%-70sL%-4i C%-3i";
/// Левый верхний угол этого окна. (0,0) - это левый верхний угол всего экрана.
#define UL_ROW 1
#define UL_COL 0
/// Самая верхняя строка экрана оставлена для вывода подскзок и т. п.
/// (т. н. строка статуса).
int main(void) {
int c_row, c_col, /// Координаты курсора на экране.
f_line, f_symb, /// Координаты курсора в файле (строка, столбец).
o_vert, o_horz; /// Смещение файла в окне редактирования.
/* Если в окне редактирования видна первая строка файла, то o_vert == 0,
а если там видна третья строка и следущие (на 2 больше), а вторая строка
оказалась выше края окна, то o_vert == 2.
Аналогично, o_horz == 0, если видна первая колонка в строке. */
int ch; /// Сюда читаем кнопки, нажатые на клавиатуре.
int i; /// Переменная цикла.
WINDOW* ed_buf; /// Здесь храним изображение всего "файла"
/// Это виртуальное окно, его часть будет показана
/// на физическом экране.
/* Вот мы и переходим от просмотра "файла" к редактированию. Опять же, для
начала создадим статический массив, в котором и будет храниться текущее
состояние файла */
char content[MAX_ROWS][MAX_COLS+1]; /// MAX_COLS симв. и \0 в конце
int len_arr[MAX_ROWS]; /// Здесь записаны длины строк.
char lin_buf[MAX_COLS+1]; /// Здесь создаем строки "1 ______ 1..." и т. д.
/// Или, в другом варианте, пустые.
char stat_lin[V_COLS+1]; /// Строка статуса
char* sp = " __________ ";
////////////////////////////////////////////////////////////////////////////////
/// Начинаем. Стартуем curses, задаем режимы.
initscr(); raw(); keypad(stdscr, TRUE); noecho();
c_row = UL_ROW; /// Курсор на экране - в лев. верх. углу
c_col = UL_COL; /// окна редактирования.
f_line = 1; /// курсор в файле - в первой строке,
f_symb = 1; /// первой колонке.
o_vert = o_horz = 0; /// файл не смещен.
/* Создаем буфер. В этом буфере будет находиться изображение нашего
"файла". В цикле заполняем буфер всякой всячиной, или пустыми строками,
в старом варианте были числа, которые соответствуют
номерам строк, чтобы было видно, как файл скроллится.
После всего надо сделать refresh()!
ОБРАТИТЕ внимание! Ширина этого буфера на единицу больше, чтобы курсор
можно было поставить на "конец строки", вот так:
The END._ */
ed_buf = newpad(MAX_ROWS, MAX_COLS+1);
lin_buf[0] = 0;
for (i=1; i<=MAX_ROWS; i++) {
/// sprintf(lin_buf, "%4i%s%4i%s%4i%s%4i%s.", i,sp, i,sp, i,sp, i,sp);
mvwaddstr(ed_buf, i-1, 0, lin_buf);
/* Здесь мы копируем созданную строку в массив строк. В этом массиве и хранится
состояние нашего "файла", а в ed_buf лежит изображение. */
strcpy(content[i-1], lin_buf); len_arr[i-1] = strlen(lin_buf);
}
refresh();
/* Заполняем строку статуса (подсказка как выйти, номер текущей строки и
колонки) выводим её на экран, ДАЛЕЕ: выводим на нужную часть экрана
нужную часть буфера, где лежит изображение нашего файла.
И в конце обновляем положение курсора. */
sprintf(stat_lin, STAT_FMT, "F10 to exit", f_line, f_symb);
mvwaddstr(stdscr, 0, 0, stat_lin);
prefresh(ed_buf,o_vert,o_horz,UL_ROW,UL_COL,UL_ROW+V_ROWS-1,UL_COL+V_COLS-1);
move(c_row, c_col);
/* Основной цикл: получаем кнопку, которую нажал юзер, если F10, то
заканчиваем цикл и переходим к завершающей части программы.
Дальше:
Пока обрабатываются только стрелки вверх и вниз. */
while (1) {
ch = wgetch(stdscr);
if (ch == KEY_F(10)) break;
/* Обрабатываем кнопки. В кейсах (case) устанавливаем все переменные, а после
switch выводим результат на экран. */
switch (ch) {
/* Вниз.
1. Если курсор в последней строке файла, то куда дальше вниз? Некуда.
Значит, ничего не надо делать:
break;
2. Если курсор НЕ в самой нижней строке окна редактирования, то его просто
надо подвинуть вниз:
c_row++;
3. А если в самой нижней строке окна, то он должен физически остаться
на том же месте, а файл надо подвинуть на строчку вверх:
o_vert++;
4. В случаях 2 и 3 курсор перемещается в файле на одну строку вниз:
f_line++;
Это первоначальный примитивный вариант обработки "стрелки вниз", сюда надо
добавить еще кое-какие проверки и действия. */
case KEY_DOWN:
if (f_line == MAX_ROWS) break;
if (c_row < UL_ROW+V_ROWS-1) c_row++;
else o_vert++;
f_line++;
break;
/* Вверх - всё аналогично. */
case KEY_UP:
if (f_line == 1) break;
if (c_row > UL_ROW) c_row--;
else o_vert--;
f_line--;
break;
/* Влево. */
case KEY_LEFT:
if (f_symb == 1) break;
if (c_col > UL_COL) c_col--;
else o_horz--;
f_symb--;
break;
/* Вправо.
Тут есть один момент. Курсор может находиться правее последнего символа.
Вот так:
Period is the last symbol in this line._
курсор ^
Поэтому: f_symb > MAX_COLS, а не f_symb == MAX_COLS.
А теперь еще изменение: вместо MAX_COLS будет len_arr[f_line-1].
Курсор не может быть правее, чем в позиции сразу за концом строки. */
case KEY_RIGHT:
if (f_symb > len_arr[f_line-1] /*MAX_COLS*/) break;
if (c_col < UL_COL+V_COLS-1) c_col++;
else o_horz++;
f_symb++;
break;
/* В начало строки */
case KEY_HOME:
if (f_symb == 1) break;
c_col = UL_COL;
o_horz = 0;
f_symb = 1;
break;
/* Обрабатываем весь остальной ввод. Пока что игнорируем всё, что меньше
пробела (32) и больше тильды (126) */
default:
if (ch<' ' || ch>'~') break;
/* вставляем символ в соответствующее место строки. Никакие проверки пока
не делаются. К этому куску разъяснения напишу потом, завтра утром. */
strcpy(lin_buf+f_symb,content[f_line-1]+f_symb-1);
lin_buf[f_symb-1]=ch;
strcpy(content[f_line-1]+f_symb-1,lin_buf+f_symb-1);
++len_arr[f_line-1];
mvwaddstr(ed_buf, f_line-1, f_symb-1, lin_buf+f_symb-1);
if (c_col < UL_COL+V_COLS-1) c_col++;
else o_horz++;
f_symb++;
break;
}
/* Наконец, после обработок:
- обновляем информацию о видимой части изображения файла, оно могло
подвинуться вверх или вниз, или вправо, или влево,
- создаем строку статуса, там мог измениться номер строки / колонки,
- определяем эту строку на законное место,
- задаем координаты курсора на экране,
- выводим всё на экран:
refresh(); */
prefresh(ed_buf,o_vert,o_horz,UL_ROW,UL_COL,UL_ROW+V_ROWS-1,UL_COL+V_COLS-1);
sprintf(stat_lin, STAT_FMT, "F10 to exit", f_line, f_symb);
mvwaddstr(stdscr, 0, 0, stat_lin);
/// sprintf(stat_lin, "offs: v %i h %i", o_vert, o_horz); /// Отладочная
/// mvwaddstr(stdscr, 0, 20, stat_lin); /// печать.
move(c_row, c_col);
refresh();
/* Цикл while заканчивается, и всё по новой... */
}
/* Завершающая часть программы.
Здесь задаются режимы работы терминала, потом работа curses завершается. */
echo(); keypad(stdscr, FALSE); cbreak(); endwin();
/// Выводим результат редактирования:
for (i=0; i<MAX_ROWS; i++) {
printf("%s\n", content[i]);
}
return 0;
}
Конечно, это еще не редактор совсем, но первая из функций собственно редактирования
уже реализована.
Просто доделать всё за пару дней точно не смогу, а может, в каком-то конкретном месте