diff options
Diffstat (limited to 'src/textadept.c')
-rw-r--r-- | src/textadept.c | 202 |
1 files changed, 168 insertions, 34 deletions
diff --git a/src/textadept.c b/src/textadept.c index 9466aed9..0f979a13 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -49,7 +49,6 @@ #include "ScintillaWidget.h" #elif CURSES #include "ScintillaTerm.h" -#include "windowman.h" #include "cdk_int.h" #include "termkey.h" #endif @@ -143,17 +142,22 @@ static ListStore *find_store, *repl_store; #define command_entry_focused gtk_widget_has_focus(command_entry) #elif CURSES // curses window. -static struct WindowManager *wm; +typedef struct Pane { + int y, x, rows, cols, split_size; // dimensions + enum {SINGLE, VSPLIT, HSPLIT} type; // pane type + WINDOW *win; // either the Scintilla curses window or the split bar's window + Scintilla *view; // Scintilla view for a non-split view + struct Pane *child1, *child2; // each pane in a split view +} Pane; // Pane implementation based on code by Chris Emerson. +static Pane *pane; static int statusbar_length[2], command_entry_focused; TermKey *ta_tk; // global for CDK use #define SS(view, msg, w, l) scintilla_send_message(view, msg, w, l) #define focus_view(view) \ (focused_view ? SS(focused_view, SCI_SETFOCUS, 0, 0) : 0, \ SS(view, SCI_SETFOCUS, 1, 0)) -/** Callback for refreshing a single Scintilla view. */ -static void r_cb(void *view, void*_) {scintilla_refresh((Scintilla *)view);} #define refresh_all() { \ - wman_walk(wm, r_cb, NULL), wman_refresh(wm); \ + pane_refresh(pane); \ if (command_entry_focused) scintilla_refresh(command_entry); \ refresh(); \ } @@ -162,8 +166,7 @@ static void r_cb(void *view, void*_) {scintilla_refresh((Scintilla *)view);} static CDKSCREEN *findbox; static CDKENTRY *find_entry, *replace_entry, *focused_entry; static char *find_text, *repl_text, *flabel, *rlabel; -typedef int FindButton; -enum FindButton {fnext_button, r_button, fprev_button, ra_button}; +typedef enum {fnext_button, r_button, fprev_button, ra_button} FindButton; static int find_options[4]; static int *match_case = &find_options[0], *whole_word = &find_options[1], *lua_pattern = &find_options[2], *in_files = &find_options[3]; @@ -403,6 +406,20 @@ static char *get_clipboard() { char *text = malloc(scintilla_get_clipboard(focused_view, NULL)); return (scintilla_get_clipboard(focused_view, text), text); } + +/** + * Redraws an entire pane and its children. + * @param pane The pane to redraw. + */ +static void pane_refresh(Pane *pane) { + if (pane->type == VSPLIT) { + mvwvline(pane->win, 0, 0, 0, pane->rows), wrefresh(pane->win); + pane_refresh(pane->child1), pane_refresh(pane->child2); + } else if (pane->type == HSPLIT) { + mvwhline(pane->win, 0, 0, 0, pane->cols), wrefresh(pane->win); + pane_refresh(pane->child1), pane_refresh(pane->child2); + } else scintilla_refresh(pane->view); +} #endif /** `find.focus()` Lua function. */ @@ -614,14 +631,14 @@ static void l_pushsplittable(lua_State *L, GtkWidget *w) { } else l_pushview(L, w); } #elif CURSES -static void l_pushsplittable(lua_State *L, struct WindowManager *wm) { - if (wm->dir != SPLIT_LEAF) { +static void l_pushsplittable(lua_State *L, Pane *pane) { + if (pane->type != SINGLE) { lua_newtable(L); - l_pushsplittable(L, wm->first), lua_rawseti(L, -2, 1); - l_pushsplittable(L, wm->second), lua_rawseti(L, -2, 2); - lua_pushboolean(L, wm->dir == SPLIT_VER), lua_setfield(L, -2, "vertical"); - lua_pushinteger(L, wm->splitpos), lua_setfield(L, -2, "size"); - } else l_pushview(L, wm->view); + l_pushsplittable(L, pane->child1), lua_rawseti(L, -2, 1); + l_pushsplittable(L, pane->child2), lua_rawseti(L, -2, 2); + lua_pushboolean(L, pane->type == VSPLIT), lua_setfield(L, -2, "vertical"); + lua_pushinteger(L, pane->split_size), lua_setfield(L, -2, "size"); + } else l_pushview(L, pane->view); } #endif @@ -632,7 +649,7 @@ static int lui_get_split_table(lua_State *L) { while (GTK_IS_PANED(gtk_widget_get_parent(w))) w = gtk_widget_get_parent(w); l_pushsplittable(L, w); #elif CURSES - l_pushsplittable(L, wm); + l_pushsplittable(L, pane); #endif return 1; } @@ -1657,7 +1674,7 @@ static void delete_view(Scintilla *view) { #if GTK /** - * Remove all Scintilla views from the given pane and delete them. + * Removes all Scintilla views from the given pane and deletes them. * @param pane The GTK pane to remove Scintilla views from. * @see delete_view */ @@ -1668,15 +1685,74 @@ static void remove_views_from_pane(GtkWidget *pane) { } #elif CURSES /** - * Signal that the curses window manager removed a view. - * @param view The view removed. + * Removes all Scintilla views from the given pane and deletes them along with + * the child panes themselves. + * @param pane The pane to remove Scintilla views from. * @see delete_view */ -static void view_removed(void *view, void*_) {delete_view((Scintilla *)view);} +static void remove_views_from_pane(Pane *pane) { + if (pane->type == VSPLIT || pane->type == HSPLIT) { + remove_views_from_pane(pane->child1), remove_views_from_pane(pane->child2); + delwin(pane->win), pane->win = NULL; // delete split bar + } else delete_view(pane->view); + free(pane); +} + +/** + * Resizes and repositions a pane. + * @param pane the pane to resize and move. + * @param rows The number of rows the pane should show. + * @param cols The number of columns the pane should show. + * @param y The y-coordinate to place the pane at. + * @param x The x-coordinate to place the pane at. + */ +static void pane_resize(Pane *pane, int rows, int cols, int y, int x) { + if (pane->type == VSPLIT) { + int ssize = pane->split_size * cols / max(pane->cols, 1); + if (ssize < 1 || ssize >= cols - 1) ssize = (ssize < 1) ? 1 : cols - 2; + pane->split_size = ssize; + pane_resize(pane->child1, rows, ssize, y, x); + pane_resize(pane->child2, rows, cols - ssize - 1, y, x + ssize + 1); + wresize(pane->win, rows, 1), mvwin(pane->win, y, x + ssize); // split bar + } else if (pane->type == HSPLIT) { + int ssize = pane->split_size * rows / max(pane->rows, 1); + if (ssize < 1 || ssize >= rows - 1) ssize = (ssize < 1) ? 1 : rows - 2; + pane->split_size = ssize; + pane_resize(pane->child1, ssize, cols, y, x); + pane_resize(pane->child2, rows - ssize - 1, cols, y + ssize + 1, x); + wresize(pane->win, 1, cols), mvwin(pane->win, y + ssize, x); // split bar + } else wresize(pane->win, rows, cols), mvwin(pane->win, y, x); + pane->rows = rows, pane->cols = cols, pane->y = y, pane->x = x; +} + +/** + * Helper for unsplitting a view. +* @param pane The pane that contains the view to unsplit. + * @param view The view to unsplit. + * @param parent The parent of pane. Used recursively. + * @see unsplit_view + */ +static int pane_unsplit_view(Pane *pane, Scintilla *view, Pane *parent) { + if (pane->type == SINGLE) { + if (pane->view != view) return FALSE; + remove_views_from_pane((pane == parent->child1) ? parent->child2 + : parent->child1); + delwin(parent->win); // delete split bar + // Inherit child's properties. + parent->type = pane->type, parent->split_size = pane->split_size; + parent->win = pane->win, parent->view = pane->view; + parent->child1 = pane->child1, parent->child2 = pane->child2; + free(pane); + // Update. + pane_resize(parent, parent->rows, parent->cols, parent->y, parent->x); + return TRUE; + } else return pane_unsplit_view(pane->child1, view, pane) || + pane_unsplit_view(pane->child2, view, pane); +} #endif /** - * Unsplits the pane a given Scintilla view is in and keeps the view. + * Unsplits the pane the given Scintilla view is in and keeps the view. * All views in the other pane are deleted. * @param view The Scintilla view to keep when unsplitting. * @see remove_views_from_pane @@ -1702,8 +1778,8 @@ static int unsplit_view(Scintilla *view) { gtk_widget_grab_focus(GTK_WIDGET(view)); g_object_unref(view), g_object_unref(other); #elif CURSES - if (wm->dir == SPLIT_LEAF) return FALSE; - wman_unsplit_view(wm, view, view_removed), scintilla_refresh(view); + if (pane->type == SINGLE) return FALSE; + pane_unsplit_view(pane, view, NULL), scintilla_refresh(view); #endif return TRUE; } @@ -1918,6 +1994,54 @@ static int lview_goto_buffer(lua_State *L) { return 0; } +#if CURSES +/** + * Creates a new pane that contains a Scintilla view. + * @param view The Scintilla view to place in the pane. + */ +static Pane *pane_new(Scintilla *view) { + Pane *p = (Pane *)calloc(1, sizeof(Pane)); + p->type = SINGLE, p->win = scintilla_get_window(view), p->view = view; + return p; +} + +/** + * Helper for splitting a view. + * Recursively propagates a split to child panes. + * @param pane The pane to split. + * @param vertical Whether to split the pane vertically or horizontally. + * @param view The first Scintilla view to place in the split view. + * @param view2 The second Scintilla view to place in the split view. + * @see split_view + */ +static int pane_split_view(Pane *pane, int vertical, Scintilla *view, + Scintilla *view2) { + if (pane->type == SINGLE) { + if (view != pane->view) return FALSE; + Pane *child1 = pane_new(view), *child2 = pane_new(view2); + pane->type = vertical ? VSPLIT : HSPLIT; + pane->child1 = child1, pane->child2 = child2, pane->view = NULL; + // Resize children and create a split bar. + if (vertical) { + pane->split_size = pane->cols / 2; + pane_resize(child1, pane->rows, pane->split_size, pane->y, pane->x); + pane_resize(child2, pane->rows, pane->cols - pane->split_size - 1, + pane->y, pane->x + pane->split_size + 1); + pane->win = newwin(pane->rows, 1, pane->y, pane->x + pane->split_size); + } else { + pane->split_size = pane->rows / 2; + pane_resize(child1, pane->split_size, pane->cols, pane->y, pane->x); + pane_resize(child2, pane->rows - pane->split_size - 1, pane->cols, + pane->y + pane->split_size + 1, pane->x); + pane->win = newwin(1, pane->cols, pane->y + pane->split_size, pane->x); + } + pane_refresh(pane); + return TRUE; + } else return pane_split_view(pane->child1, vertical, view, view2) || + pane_split_view(pane->child2, vertical, view, view2); +} +#endif + /** * Splits the given Scintilla view into two views. * The new view shows the same document as the original one. @@ -1951,7 +2075,7 @@ static void split_view(Scintilla *view, int vertical) { while (gtk_events_pending()) gtk_main_iteration(); // ensure view2 is painted #elif CURSES Scintilla *view2 = new_view(curdoc); - wman_split_view(wm, vertical, view, view2, scintilla_get_window(view2)); + pane_split_view(pane, vertical, view, view2); #endif focus_view(view2); SS(view2, SCI_SETSEL, anchor, current_pos); @@ -1971,6 +2095,20 @@ static int lview_unsplit(lua_State *L) { return (lua_pushboolean(L, unsplit_view(lL_checkview(L, 1))), 1); } +#if CURSES +/** + * Searches for the given view and returns its parent pane. + * @param pane The pane that contains the desired view. + * @param view The view to get the parent pane of. + */ +static Pane *pane_get_parent(Pane *pane, Scintilla *view) { + if (pane->type == SINGLE) return NULL; + if (pane->child1->view == view || pane->child2->view == view) return pane; + Pane *parent = pane_get_parent(pane->child1, view); + return parent ? parent : pane_get_parent(pane->child2, view); +} +#endif + /** `view.__index` Lua metatable. */ static int lview__index(lua_State *L) { const char *key = lua_tostring(L, 2); @@ -1984,8 +2122,8 @@ static int lview__index(lua_State *L) { lua_pushinteger(L, pos); } else lua_pushnil(L); #elif CURSES - struct WindowManager *parent = wman_parent(wm, wman_view_owner(wm, view)); - parent ? lua_pushinteger(L, parent->splitpos) : lua_pushnil(L); + Pane *parent = pane_get_parent(pane, view); + parent ? lua_pushinteger(L, parent->split_size) : lua_pushnil(L); #endif } else lua_rawget(L, 1); return 1; @@ -2003,9 +2141,8 @@ static int lview__newindex(lua_State *L) { GtkWidget *pane = gtk_widget_get_parent(lL_checkview(L, 1)); if (GTK_IS_PANED(pane)) gtk_paned_set_position(GTK_PANED(pane), size); #elif CURSES - Scintilla *view = lL_checkview(L, 1); - struct WindowManager *split = wman_parent(wm, wman_view_owner(wm, view)); - if (split) wman_move_split(split, size); + Pane *p = pane_get_parent(pane, lL_checkview(L, 1)); + if (p) p->split_size = size, pane_resize(p, p->rows, p->cols, p->y, p->x); #endif } else lua_rawset(L, 1); return 0; @@ -2219,9 +2356,7 @@ static void new_window() { dummy_view = scintilla_new(); #elif CURSES - Scintilla *view = new_view(0); - wm = wman_create(view, scintilla_get_window(view)); - wman_resize(wm, LINES - 2, COLS, 1, 0); + pane = pane_new(new_view(0)), pane_resize(pane, LINES - 2, COLS, 1, 0); command_entry = scintilla_new(NULL); wresize(scintilla_get_window(command_entry), 1, COLS); mvwin(scintilla_get_window(command_entry), LINES - 2, 0); @@ -2235,8 +2370,7 @@ static void new_window() { static void resize(int signal) { struct winsize win; ioctl(0, TIOCGWINSZ, &win); - resizeterm(win.ws_row, win.ws_col); - wman_resize(wm, LINES - 2, COLS, 1, 0); + resizeterm(win.ws_row, win.ws_col), pane_resize(pane, LINES - 2, COLS, 1, 0); WINDOW *ce_win = scintilla_get_window(command_entry); wresize(ce_win, 1, COLS), mvwin(ce_win, LINES - 1 - getmaxy(ce_win), 0); lL_event(lua, "update_ui", -1); @@ -2423,7 +2557,7 @@ int main(int argc, char **argv) { if (quit && !lL_event(lua, "quit", -1)) { l_close(lua); // Free some memory. - free(flabel), free(rlabel); + free(pane), free(flabel), free(rlabel); if (find_text) free(find_text); if (repl_text) free(repl_text); for (int i = 0; i < 10; i++) { |