diff --git a/src/Makefile b/src/Makefile index ac73445..8541337 100644 --- a/src/Makefile +++ b/src/Makefile @@ -30,8 +30,8 @@ endif collascii: frontend.out mv frontend.out collascii -frontend.out: cursor.o fe_modes.o frontend.out: LDLIBS +=-lncurses +frontend.out: cursor.o fe_modes.o canvas.o view.o ## PATTERNS diff --git a/src/canvas.c b/src/canvas.c index 55c9f77..c172df1 100644 --- a/src/canvas.c +++ b/src/canvas.c @@ -3,15 +3,11 @@ * TODO: add save/read from file options * TODO: assert get/set positions are within canvas sizes? */ +#include "canvas.h" #include #include #include -typedef struct { - int num_cols, num_rows; - char **rows; -} Canvas; - /* Create a canvas object * * Returned pointer should be freed with free_canvas @@ -27,6 +23,14 @@ Canvas *canvas_new(int rows, int cols) { return canvas; } +Canvas *canvas_new_blank(int rows, int cols) { + Canvas *canvas = canvas_new(rows, cols); + for (int i = 0; i < (rows * cols); i++) { + canvas_schari(canvas, i, ' '); + } + return canvas; +} + /* Create and return a deep copy of a canvas * * Returned pointer should be freed with free_canvas diff --git a/src/canvas.h b/src/canvas.h index 480dd6e..f48a0db 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -9,6 +9,8 @@ typedef struct { } Canvas; Canvas *canvas_new(int rows, int cols); +Canvas *canvas_new_blank(int rows, int cols); + Canvas *canvas_cpy(Canvas *orig); void canvas_free(Canvas *canvas); void canvas_scharyx(Canvas *canvas, int y, int x, char c); diff --git a/src/cursor.c b/src/cursor.c index 2d9860c..7d946e8 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -1,63 +1,68 @@ #include "cursor.h" #include #include +#include "frontend.h" /* The Cursor struct helps with controls. * It also maps the drawing area to the canvas nicely. */ -Cursor* cursor_new() { - Cursor* cursor = malloc(sizeof(Cursor)); +Cursor *cursor_new() { + Cursor *cursor = malloc(sizeof(Cursor)); cursor->x = 0; cursor->y = 0; return cursor; } -void cursor_move_up(Cursor* cursor) { +void cursor_move_up(Cursor *cursor, View *view) { if (cursor->y == 0) { + view_move_up(view); return; } cursor->y--; } -void cursor_move_down(Cursor* cursor) { - if (cursor->y == (LINES - 5)) { // Take box lines into account +void cursor_move_down(Cursor *cursor, View *view) { + if (cursor->y == view_max_y) { + view_move_down(view); return; } cursor->y++; } -void cursor_move_left(Cursor* cursor) { +void cursor_move_left(Cursor *cursor, View *view) { if (cursor->x == 0) { + view_move_left(view); return; } cursor->x--; } -void cursor_move_right(Cursor* cursor) { - if (cursor->x == (COLS - 3)) { // Take box lines into account +void cursor_move_right(Cursor *cursor, View *view) { + if (cursor->x == view_max_x) { + view_move_right(view); return; } cursor->x++; } -int cursor_x_to_canvas(Cursor* cursor) { return cursor->x + 1; } +int cursor_x_to_canvas(Cursor *cursor) { return cursor->x + 1; } -int cursor_y_to_canvas(Cursor* cursor) { return cursor->y + 1; } +int cursor_y_to_canvas(Cursor *cursor) { return cursor->y + 1; } -void cursor_key_to_move(int arrow, Cursor* cursor) { +void cursor_key_to_move(int arrow, Cursor *cursor, View *view) { switch (arrow) { case KEY_LEFT: - cursor_move_left(cursor); + cursor_move_left(cursor, view); break; case KEY_RIGHT: - cursor_move_right(cursor); + cursor_move_right(cursor, view); break; case KEY_UP: - cursor_move_up(cursor); + cursor_move_up(cursor, view); break; case KEY_DOWN: - cursor_move_down(cursor); + cursor_move_down(cursor, view); break; } } @@ -76,4 +81,4 @@ int cursor_opposite_dir(int arrow) { return -1; } -void cursor_free(Cursor* cursor) { free(cursor); } +void cursor_free(Cursor *cursor) { free(cursor); } diff --git a/src/cursor.h b/src/cursor.h index 10ec275..6580107 100644 --- a/src/cursor.h +++ b/src/cursor.h @@ -5,22 +5,27 @@ * It also maps the drawing area to the canvas nicely. */ +#include "view.h" + +#include +#include + typedef struct CURSOR { int x; int y; } Cursor; -Cursor* cursor_new(); -void cursor_free(Cursor* cursor); +Cursor *cursor_new(); +void cursor_free(Cursor *cursor); -void cursor_move_up(Cursor* cursor); -void cursor_move_down(Cursor* cursor); -void cursor_move_left(Cursor* cursor); -void cursor_move_right(Cursor* cursor); +void cursor_move_up(Cursor *cursor, View *view); +void cursor_move_down(Cursor *cursor, View *view); +void cursor_move_left(Cursor *cursor, View *view); +void cursor_move_right(Cursor *cursor, View *view); -int cursor_x_to_canvas(Cursor* cursor); -int cursor_y_to_canvas(Cursor* cursor); -void cursor_key_to_move(int arrow, Cursor* cursor); +int cursor_x_to_canvas(Cursor *cursor); +int cursor_y_to_canvas(Cursor *cursor); +void cursor_key_to_move(int arrow, Cursor *cursor, View *view); int cursor_opposite_dir(int arrow); #endif diff --git a/src/fe_modes.c b/src/fe_modes.c index 9f7eb5f..60e6649 100644 --- a/src/fe_modes.c +++ b/src/fe_modes.c @@ -15,6 +15,7 @@ int (*mode_functions[])(State *, WINDOW *, WINDOW *) = { mode_picker, mode_insert, + mode_pan, }; ////////////////////////////// @@ -49,6 +50,7 @@ int mode_picker(State *state, WINDOW *canvas_win, WINDOW *status_win) { // Mode Switch - Enter to canvas, if (state->ch_in == KEY_ENTER) { state->current_mode = state->last_canvas_mode; + return 0; } // LR Arrows navigation @@ -73,22 +75,20 @@ int mode_insert(State *state, WINDOW *canvas_win, WINDOW *status_win) { // insert mode behavior if ((state->ch_in == KEY_LEFT) || (state->ch_in == KEY_RIGHT) || (state->ch_in == KEY_UP) || (state->ch_in == KEY_DOWN)) { - cursor_key_to_move(state->ch_in, state->cursor); + cursor_key_to_move(state->ch_in, state->cursor, state->view); state->last_arrow_direction = state->ch_in; } else { if (' ' <= state->ch_in && state->ch_in <= '~') { // check if ch is printable - mvwaddch(canvas_win, cursor_y_to_canvas(state->cursor), - cursor_x_to_canvas(state->cursor), state->ch_in); - cursor_key_to_move(state->last_arrow_direction, state->cursor); + front_setcharcursor(state->ch_in); + cursor_key_to_move(state->last_arrow_direction, state->cursor, + state->view); } else if (state->ch_in == KEY_BACKSPACE) { cursor_key_to_move(cursor_opposite_dir(state->last_arrow_direction), - state->cursor); - mvwaddch(canvas_win, cursor_y_to_canvas(state->cursor), - cursor_x_to_canvas(state->cursor), ' '); + state->cursor, state->view); + front_setcharcursor(' '); } else if (state->ch_in == KEY_DC) { - mvwaddch(canvas_win, cursor_y_to_canvas(state->cursor), - cursor_x_to_canvas(state->cursor), ' '); + front_setcharcursor(' '); } else { // Print non-print characters to bottom left in status_win bar mvwaddch(status_win, 1, COLS - 3, state->ch_in); @@ -101,6 +101,28 @@ int mode_insert(State *state, WINDOW *canvas_win, WINDOW *status_win) { return 0; } +/* Mode Pan + + * Pans the View with arrow keys + */ +int mode_pan(State *state, WINDOW *canvas_win, WINDOW *status_win) { + // handle mode changing + if (state->ch_in == KEY_TAB) { + // Clean up code + state->last_canvas_mode = MODE_PAN; + + state->current_mode = MODE_PICKER; + return 0; + } + + if ((state->ch_in == KEY_LEFT) || (state->ch_in == KEY_RIGHT) || + (state->ch_in == KEY_UP) || (state->ch_in == KEY_DOWN)) { + view_pan_ch(state->ch_in, state->view); + } + + return 0; +} + //////////////////////////// // SPECIFC MODE FUNCTIONS // //////////////////////////// diff --git a/src/fe_modes.h b/src/fe_modes.h index 7b52bf1..2c4563b 100644 --- a/src/fe_modes.h +++ b/src/fe_modes.h @@ -6,6 +6,7 @@ int mode_picker(State *state, WINDOW *canvas_win, WINDOW *status_win); int mode_insert(State *state, WINDOW *canvas_win, WINDOW *status_win); +int mode_pan(State *state, WINDOW *canvas_win, WINDOW *status_win); extern int (*mode_functions[])(State *, WINDOW *, WINDOW *); diff --git a/src/frontend.c b/src/frontend.c index 3c44ca8..73a22f9 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -1,12 +1,17 @@ #include #include +#include "canvas.h" #include "cursor.h" #include "fe_modes.h" #include "frontend.h" #include "mode_id.h" #include "state.h" +WINDOW *canvas_win, *status_win; +Cursor *cursor; +View *view; + /* Layout * ___________________________________________ * | 0 -- X, COLS | canvas window @@ -24,8 +29,6 @@ * */ -int STATUS_HEIGHT = 1; // not including borders - int main(int argc, char *argv[]) { /* initialize your non-curses data structures here */ @@ -38,19 +41,24 @@ int main(int argc, char *argv[]) { (void)noecho(); /* don't print on getch() */ curs_set(2); + define_key("\r", KEY_ENTER); // Bind the key properly + if (has_colors()) { setup_colors(); } - WINDOW *canvas_win = create_canvas_win(); - WINDOW *status_win = create_status_win(); + canvas_win = create_canvas_win(); + status_win = create_status_win(); + + cursor = cursor_new(); + Canvas *canvas = canvas_new_blank(1000, 1000); + + view = view_new_startpos(canvas, 300, 300); // Enable keyboard mapping keypad(canvas_win, TRUE); keypad(status_win, TRUE); - Cursor *cursor = cursor_new(); - char test_msg[] = "Test mode"; print_status(test_msg, status_win); @@ -64,9 +72,9 @@ int main(int argc, char *argv[]) { .ch_in = 0, .cursor = cursor, .current_mode = MODE_INSERT, - .last_arrow_direction = KEY_RIGHT, .last_canvas_mode = MODE_INSERT, + .view = view, }; State *state = &new_state; @@ -75,8 +83,9 @@ int main(int argc, char *argv[]) { mode_functions[state->current_mode](state, canvas_win, status_win); - wrefresh(status_win); - wrefresh(canvas_win); // Refresh Canvas last so it gets the cursor + update_screen_size(canvas_win, status_win, cursor); + + refresh_screen(); } // Cleanup @@ -100,28 +109,74 @@ void setup_colors() { init_pair(7, COLOR_BLACK, COLOR_WHITE); } -WINDOW *create_newwin(int height, int width, int starty, int startx, - int should_draw_box) { - WINDOW *local_win; - - local_win = newwin(height, width, starty, startx); +void front_setcharcursor(char ch) { + canvas_scharyx(view->canvas, cursor_y_to_canvas(cursor) - 1 + view->y, + cursor_x_to_canvas(cursor) - 1 + view->x, ch); +} - if (should_draw_box) { - box(local_win, 0, 0); /* 0, 0 gives default characters - * for the vertical and horizontal - * lines */ - wrefresh(local_win); /* Show that box */ +void redraw_canvas_win() { + for (int x = 0; x < view_max_x; x++) { + for (int y = 0; y < view_max_y; y++) { + mvwaddch(canvas_win, y + 1, x + 1, + canvas_gcharyx(view->canvas, y + view->y, x + view->x)); + } } +} - return local_win; +void refresh_screen() { + update_screen_size(); + redraw_canvas_win(); + wmove(canvas_win, cursor_y_to_canvas(cursor), cursor_x_to_canvas(cursor)); + + wrefresh(status_win); + wrefresh(canvas_win); // Refresh Canvas last so it gets the cursor +} + +void update_screen_size() { + static int window_h_old, window_w_old; + + int window_h_new, window_w_new; + + getmaxyx(stdscr, window_h_new, window_w_new); + + if (window_h_new != window_h_old || window_w_new != window_w_old) { + window_h_old = window_h_new; + window_w_old = window_w_new; + + wresize(canvas_win, window_h_new - (STATUS_HEIGHT + 1), window_w_new); + wresize(status_win, STATUS_HEIGHT + 2, window_w_new); + + mvwin(status_win, window_h_new - (STATUS_HEIGHT + 2), 0); + + wclear(stdscr); + wclear(canvas_win); + wclear(status_win); + + // Redraw borders + wborder(status_win, ACS_VLINE, ACS_VLINE, ACS_HLINE, + ACS_HLINE, // Sides: ls, rs, ts, bs, + ACS_LTEE, ACS_RTEE, ACS_LLCORNER, + ACS_LRCORNER); // Corners: tl, tr, bl, br + wborder(canvas_win, ACS_VLINE, ACS_VLINE, ACS_HLINE, + ACS_HLINE, // Sides: ls, rs, ts, bs, + ACS_ULCORNER, ACS_URCORNER, ACS_LTEE, + ACS_RTEE); // Corners: tl, tr, bl, br + + // Move cursor inside the canvas + if (cursor->x >= view_max_x) { + cursor->x = view_max_x; + } + if (cursor->y >= view_max_y) { + cursor->y = view_max_y; + } + } } WINDOW *create_canvas_win() { WINDOW *local_win; // + 1 due to bottom border - local_win = newwin(LINES - (STATUS_HEIGHT + 1), COLS, 0, - 0); // height, width, starty, startx + local_win = newwin(LINES - (STATUS_HEIGHT + 1), COLS, 0, 0); wborder(local_win, ACS_VLINE, ACS_VLINE, ACS_HLINE, ACS_HLINE, // Sides: ls, rs, ts, bs, @@ -155,7 +210,7 @@ void destroy_win(WINDOW *local_win) { } int print_status(char *str, WINDOW *window) { - wattrset(window, COLOR_PAIR(7)); + // wattrset(window, COLOR_PAIR(7)); return mvwprintw(window, 1, 1, str); } diff --git a/src/frontend.h b/src/frontend.h index 42f2f2c..89b0a21 100644 --- a/src/frontend.h +++ b/src/frontend.h @@ -2,20 +2,22 @@ #define frontend_h #include +#include "cursor.h" +#include "view.h" #define KEY_TAB '\t' #define KEY_SHIFT_TAB KEY_BTAB -// #define KEY_ENTER '\r' // May be wrong in ncurses void finish(int sig); void setup_colors(); +void update_screen_size(); +void refresh_screen(); +void redraw_canvas_win(); +void front_setcharcursor(char ch); WINDOW *create_canvas_win(); WINDOW *create_status_win(); void destroy_win(); int print_status(char *str, WINDOW *window); -WINDOW *create_newwin(int height, int width, int starty, int startx, - int should_draw_box); - #endif diff --git a/src/mode_id.h b/src/mode_id.h index 0103f05..45f7c91 100644 --- a/src/mode_id.h +++ b/src/mode_id.h @@ -8,6 +8,7 @@ typedef enum { MODE_PICKER, MODE_INSERT, + MODE_PAN, // ^ add your mode above LAST, // used to get number of elements diff --git a/src/state.h b/src/state.h index 468b3da..13858a5 100644 --- a/src/state.h +++ b/src/state.h @@ -3,6 +3,7 @@ #include "cursor.h" #include "mode_id.h" +#include "view.h" /* State keeps track of changing variables for mode functions. * If you add something, don't forget to also add an init before the main loop. @@ -17,6 +18,7 @@ typedef struct { Mode_ID last_canvas_mode; int last_arrow_direction; + View *view; } State; #endif diff --git a/src/view.c b/src/view.c new file mode 100644 index 0000000..699379d --- /dev/null +++ b/src/view.c @@ -0,0 +1,63 @@ +#include "view.h" + +View *view_new(Canvas *canvas) { + View *view = malloc(sizeof(View)); + view->x = 0; + view->y = 0; + view->canvas = canvas; + return view; +} + +View *view_new_startpos(Canvas *canvas, int x, int y) { + View *view = view_new(canvas); + view->x = x; + view->y = y; + return view; +} + +void view_move_up(View *view) { + if (view->y == 0) { + return; + } + view->y--; +} + +void view_move_down(View *view) { + if (view->y - view_max_y == view->canvas->num_rows) { + return; + } + view->y++; +} + +void view_move_left(View *view) { + if (view->x == 0) { + return; + } + view->x--; +} + +void view_move_right(View *view) { + if (view->x + view_max_x == view->canvas->num_cols) { + return; + } + view->x++; +} + +void view_pan_ch(int arrow, View *view) { + switch (arrow) { + case KEY_LEFT: + view_move_left(view); + break; + case KEY_RIGHT: + view_move_right(view); + break; + case KEY_UP: + view_move_up(view); + break; + case KEY_DOWN: + view_move_down(view); + break; + } +} + +void view_free(View *view) { free(view); } diff --git a/src/view.h b/src/view.h new file mode 100644 index 0000000..76b58cb --- /dev/null +++ b/src/view.h @@ -0,0 +1,27 @@ +#ifndef view_h +#define view_h + +#include +#include +#include "canvas.h" + +#define STATUS_HEIGHT 2 // not including borders +#define view_max_x (COLS - 3) +#define view_max_y (LINES - 4 - STATUS_HEIGHT) + +typedef struct { + int x, y; + Canvas *canvas; +} View; + +View *view_new(Canvas *canvas); +View *view_new_startpos(Canvas *canvas, int x, int y); + +void view_move_up(View *view); +void view_move_down(View *view); +void view_move_left(View *view); +void view_move_right(View *view); + +void view_pan_ch(int ch, View *view); + +#endif