From: Michael D. Lowis Date: Fri, 3 Feb 2017 14:08:10 +0000 (-0500) Subject: xedit.c is now 100% functional using the new win ui layer. unit tests no longer build... X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=e6101027b78e005484fd49440c960d04df2aee99;p=projs%2Ftide.git xedit.c is now 100% functional using the new win ui layer. unit tests no longer build though. Need to rethink how those work. --- diff --git a/inc/win.h b/inc/win.h index 1c9b7fe..684cbf0 100644 --- a/inc/win.h +++ b/inc/win.h @@ -36,7 +36,11 @@ void win_setmouse(MouseConfig* mconfig); View* win_view(WinRegion id); Buf* win_buf(WinRegion id); Sel* win_sel(WinRegion id); +bool win_btnpressed(MouseBtn btn); +WinRegion win_getregion(void); +void win_setregion(WinRegion id); +void onupdate(void); void mouse_left(WinRegion id, size_t count, size_t row, size_t col); void mouse_middle(WinRegion id, size_t count, size_t row, size_t col); void mouse_right(WinRegion id, size_t count, size_t row, size_t col); diff --git a/lib/view.c b/lib/view.c index e01c036..be6dc24 100644 --- a/lib/view.c +++ b/lib/view.c @@ -71,7 +71,7 @@ static size_t fill_row(View* view, unsigned row, size_t pos) { } static void reflow(View* view) { - if (!view->nrows) return; + if (!view->rows) return; size_t pos = view->rows[0]->off; for (size_t y = 0; y < view->nrows; y++) pos = fill_row(view, y, pos); diff --git a/lib/win.c b/lib/win.c index b333781..24309b7 100644 --- a/lib/win.c +++ b/lib/win.c @@ -1,8 +1,8 @@ #include #include #include -#include #include +#include static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols); static WinRegion getregion(size_t x, size_t y); @@ -36,8 +36,7 @@ static ButtonState MouseBtns[MOUSE_BTN_COUNT] = {0}; KeyBinding* Keys = NULL; void win_init(char* name) { - win_settext(STATUS, "This is a status line"); - for (int i = 1; i < SCROLL; i++) + for (int i = 0; i < SCROLL; i++) view_init(&(Regions[i].view), NULL); x11_init(&Config); Font = x11_font_load(FONTNAME); @@ -50,15 +49,31 @@ void win_loop(void) { } void win_settext(WinRegion id, char* text) { - view_init(win_view(id), NULL); - view_putstr(win_view(id), text); - view_selprev(win_view(id)); // clear the selection + View* view = win_view(id); + view->buffer.gapstart = view->buffer.bufstart; + view->buffer.gapend = view->buffer.bufend; + view->selection = (Sel){0,0,0}; + view_putstr(view, text); + view_selprev(view); // clear the selection + buf_logclear(&(view->buffer)); } void win_setkeys(KeyBinding* bindings) { Keys = bindings; } +bool win_btnpressed(MouseBtn btn) { + return MouseBtns[btn].pressed; +} + +WinRegion win_getregion(void) { + return Focused; +} + +void win_setregion(WinRegion id) { + Focused = id; +} + View* win_view(WinRegion id) { if (id == FOCUSED) id = Focused; return &(Regions[id].view); @@ -114,7 +129,7 @@ static void layout(int width, int height) { static void onredraw(int width, int height) { size_t fheight = x11_font_height(Font); size_t fwidth = x11_font_width(Font); - + onupdate(); // Let the user program update the status and such /* layout and draw the three text regions */ layout(width, height); for (int i = 0; i < SCROLL; i++) { @@ -151,7 +166,7 @@ static void oninput(int mods, Rune key) { if (key == '\n' && win_view(FOCUSED)->buffer.crlf) key = RUNE_CRLF; /* search for a key binding entry */ uint32_t mkey = tolower(key); - for (KeyBinding* bind = Keys; bind && bind->mods; bind++) { + for (KeyBinding* bind = Keys; bind && bind->key; bind++) { if ((mkey == bind->key) && (bind->mods == ModAny || bind->mods == mods)) { bind->action(); return; @@ -166,18 +181,19 @@ static void oninput(int mods, Rune key) { static void onmouse(MouseAct act, MouseBtn btn, int x, int y) { WinRegion id = getregion(x, y); - if (id != TAGS && id != EDIT) return; - if (Focused != id) Focused = id; size_t row = (y-Regions[id].y) / x11_font_height(Font); size_t col = (x-Regions[id].x) / x11_font_width(Font); if (act == MOUSE_ACT_MOVE) { if (MouseBtns[MOUSE_BTN_LEFT].pressed) { + WinRegion selid = MouseBtns[MOUSE_BTN_LEFT].region; if (MouseBtns[MOUSE_BTN_LEFT].count == 1) { - view_setcursor(win_view(id), row, col); + view_setcursor(win_view(selid), row, col); MouseBtns[MOUSE_BTN_LEFT].count = 0; } else { - view_selext(win_view(id), row, col); + view_selext(win_view(selid), row, col); } + } else if (id == TAGS || id == EDIT) { + Focused = id; } } else { MouseBtns[btn].pressed = (act == MOUSE_ACT_DOWN); @@ -194,7 +210,7 @@ static void onmouse(MouseAct act, MouseBtn btn, int x, int y) { } else if (MouseBtns[btn].count > 0) { /* execute the action on button release */ if (MouseActs[btn]) - MouseActs[btn](id, MouseBtns[btn].count, row, col); + MouseActs[btn](MouseBtns[btn].region, MouseBtns[btn].count, row, col); } } } @@ -248,4 +264,3 @@ static WinRegion getregion(size_t x, size_t y) { return NREGIONS; } - diff --git a/term.c b/term.c index ca167f2..8c5a877 100644 --- a/term.c +++ b/term.c @@ -8,15 +8,15 @@ /* Mouse Handling *****************************************************************************/ void mouse_left(WinRegion id, size_t count, size_t row, size_t col) { - + } void mouse_middle(WinRegion id, size_t count, size_t row, size_t col) { - + } void mouse_right(WinRegion id, size_t count, size_t row, size_t col) { - + } MouseConfig* MouseHandlers[NREGIONS] = { @@ -32,6 +32,10 @@ MouseConfig* MouseHandlers[NREGIONS] = { /* Main Routine *****************************************************************************/ + +void onupdate(void) { +} + int main(int argc, char** argv) { win_init("term"); //win_setkeys(&Bindings); @@ -65,7 +69,7 @@ int main(int argc, char** argv) { layout(width, height); x11_draw_rect(CLR_BASE03, 0, 0, width, height); x11_draw_rect(CLR_BASE02, 0, Regions[EDIT].y-2, fwidth + 4, height - Regions[EDIT].y + 2); - + draw_status(CLR_BASE1, (width - 4) / x11_font_width(Font)); draw_region(TAGS); draw_region(EDIT); @@ -187,7 +191,7 @@ static KeyBinding Bindings[] = { { ModAny, KEY_DOWN, cursor_dn }, { ModAny, KEY_LEFT, cursor_left }, { ModAny, KEY_RIGHT, cursor_right }, - + /* Standard Unix Shortcuts */ { ModCtrl, 'u', del_to_bol }, { ModCtrl, 'k', del_to_eol }, @@ -336,23 +340,6 @@ static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t } static void draw_status(int fg, size_t ncols) { - Buf* buf = getbuf(EDIT); - UGlyph glyphs[ncols], *status = glyphs; - (status++)->rune = (buf->charset == BINARY ? 'B' : 'U'); - (status++)->rune = (buf->crlf ? 'C' : 'N'); - (status++)->rune = (buf->expand_tabs ? 'S' : 'T'); - (status++)->rune = (buf->copy_indent ? 'I' : 'i'); - (status++)->rune = (SearchDir < 0 ? '<' : '>'); - (status++)->rune = (buf->modified ? '*' : ' '); - (status++)->rune = ' '; - char* path = (buf->path ? buf->path : "*scratch*"); - size_t len = strlen(path); - if (len > ncols-4) { - (status++)->rune = '.'; - (status++)->rune = '.'; - (status++)->rune = '.'; - path += (len - ncols) + 6; - } while (*path) (status++)->rune = *path++; draw_runes(2, 2, fg, 0, glyphs, status - glyphs); @@ -418,7 +405,7 @@ static void redraw(int width, int height) { layout(width, height); x11_draw_rect(CLR_BASE03, 0, 0, width, height); x11_draw_rect(CLR_BASE02, 0, Regions[EDIT].y-2, fwidth + 4, height - Regions[EDIT].y + 2); - + draw_status(CLR_BASE1, (width - 4) / x11_font_width(Font)); draw_region(TAGS); draw_region(EDIT); @@ -435,19 +422,19 @@ static void delete(void) { static void del_to_bol(void) { view_bol(currview(), true); - if (view_selsize(currview()) > 0) + if (view_selsize(currview()) > 0) delete(); } static void del_to_eol(void) { view_eol(currview(), true); - if (view_selsize(currview()) > 0) + if (view_selsize(currview()) > 0) delete(); } static void del_to_bow(void) { view_byword(currview(), LEFT, true); - if (view_selsize(currview()) > 0) + if (view_selsize(currview()) > 0) delete(); } @@ -606,10 +593,10 @@ static void cmd_exec(char* cmd) { if (op != '<') dest = Focused; output = cmdread(ShellCmd, &error); } - + if (error) view_append(getview(TAGS), chomp(error)); - + if (output) { if (op == '>') view_append(getview(dest), chomp(output)); diff --git a/xedit.c b/xedit.c index 1d2b0a0..b8d5769 100644 --- a/xedit.c +++ b/xedit.c @@ -3,565 +3,309 @@ #include #include #include +#include #ifdef TEST #define exit mockexit #endif -enum RegionId { - STATUS = 0, - TAGS = 1, - EDIT = 2, - NREGIONS = 3 -}; - -// Input Handlers -static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y); -static void key_handler(int mods, Rune key); - -// Drawing Routines -static void draw_runes(size_t x, size_t y, int fg, int bg, UGlyph* glyphs, size_t rlen); -static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols); -static void draw_status(int fg, size_t ncols); -static void draw_region(enum RegionId id); -static void layout(int width, int height); -static void redraw(int width, int height); - -// UI Callbacks -static void delete(void); -static void del_to_bol(void); -static void del_to_eol(void); -static void del_to_bow(void); -static void backspace(void); -static void cursor_bol(void); -static void cursor_eol(void); -static void cursor_home(void); -static void cursor_end(void); -static void cursor_up(void); -static void cursor_dn(void); -static void cursor_left(void); -static void cursor_right(void); -static void page_up(void); -static void page_dn(void); -static void select_prev(void); -static void change_focus(void); -static void quit(void); -static void save(void); -static void undo(void); -static void redo(void); -static void tag_undo(void); -static void tag_redo(void); -static void cut(void); -static void copy(void); -static void paste(void); -static void search(void); -static void execute(void); -static void find(char* arg); -static void open_file(void); -static void pick_ctag(void); -static void goto_ctag(void); -static void tabs(void); -static void indent(void); -static void del_indent(void); -static void add_indent(void); -static void eol_mode(void); -static void debug_dump(void); -static void new_win(void); - -// Tag/Cmd Execution -static Tag* tag_lookup(char* cmd); -static void tag_exec(Tag* tag, char* arg); -static void cmd_exec(char* cmd); -static void exec(char* cmd); - -// Mouse Handling -static void mouse_left(enum RegionId id, size_t count, size_t row, size_t col); -static void mouse_middle(enum RegionId id, size_t count, size_t row, size_t col); -static void mouse_right(enum RegionId id, size_t count, size_t row, size_t col); -static void mouse_wheelup(enum RegionId id, size_t count, size_t row, size_t col); -static void mouse_wheeldn(enum RegionId id, size_t count, size_t row, size_t col); - -// Region Utils -static View* getview(enum RegionId id); -static Buf* getbuf(enum RegionId id); -static View* currview(void); -static Buf* currbuf(void); -static enum RegionId getregion(size_t x, size_t y); -static Sel* getsel(enum RegionId id); -static Sel* currsel(void); - -/* Global Data - *****************************************************************************/ -static int SearchDir = DOWN; -static enum RegionId Focused = EDIT; -static Region Regions[NREGIONS] = { 0 }; -static ButtonState MouseBtns[MOUSE_BTN_COUNT] = { 0 }; -static XFont Font; -static XConfig Config = { - .redraw = redraw, - .handle_key = key_handler, - .handle_mouse = mouse_handler, - .shutdown = quit, - .palette = COLOR_PALETTE -}; - -Tag Builtins[] = { - { .tag = "Quit", .action.noarg = quit }, - { .tag = "Save", .action.noarg = save }, - { .tag = "Cut", .action.noarg = cut }, - { .tag = "Copy", .action.noarg = copy }, - { .tag = "Paste", .action.noarg = paste }, - { .tag = "Undo", .action.noarg = tag_undo }, - { .tag = "Redo", .action.noarg = tag_redo }, - { .tag = "Find", .action.arg = find }, - { .tag = "Tabs", .action.noarg = tabs }, - { .tag = "Indent", .action.noarg = indent }, - { .tag = "Eol", .action.noarg = eol_mode }, - { .tag = NULL, .action.noarg = NULL } -}; - -void (*MouseActs[MOUSE_BTN_COUNT])(enum RegionId id, size_t count, size_t row, size_t col) = { - [MOUSE_BTN_LEFT] = mouse_left, - [MOUSE_BTN_MIDDLE] = mouse_middle, - [MOUSE_BTN_RIGHT] = mouse_right, - [MOUSE_BTN_WHEELUP] = mouse_wheelup, - [MOUSE_BTN_WHEELDOWN] = mouse_wheeldn, -}; - -static KeyBinding Bindings[] = { - /* Cursor Movements */ - { ModAny, KEY_HOME, cursor_home }, - { ModAny, KEY_END, cursor_end }, - { ModAny, KEY_UP, cursor_up }, - { ModAny, KEY_DOWN, cursor_dn }, - { ModAny, KEY_LEFT, cursor_left }, - { ModAny, KEY_RIGHT, cursor_right }, - - /* Standard Unix Shortcuts */ - { ModCtrl, 'u', del_to_bol }, - { ModCtrl, 'k', del_to_eol }, - { ModCtrl, 'w', del_to_bow }, - { ModCtrl, 'h', backspace }, - { ModCtrl, 'a', cursor_bol }, - { ModCtrl, 'e', cursor_eol }, - - /* Standard Text Editing Shortcuts */ - { ModCtrl, 's', save }, - { ModCtrl, 'z', undo }, - { ModCtrl, 'y', redo }, - { ModCtrl, 'x', cut }, - { ModCtrl, 'c', copy }, - { ModCtrl, 'v', paste }, - - /* Block Indent */ - { ModCtrl, '[', del_indent }, - { ModCtrl, ']', add_indent }, - - /* Common Special Keys */ - { ModNone, KEY_PGUP, page_up }, - { ModNone, KEY_PGDN, page_dn }, - { ModAny, KEY_DELETE, delete }, - { ModAny, KEY_BACKSPACE, backspace }, - - /* Implementation Specific */ - { ModNone, KEY_ESCAPE, select_prev }, - { ModCtrl, KEY_ESCAPE, debug_dump }, - { ModCtrl, 't', change_focus }, - { ModCtrl, 'q', quit }, - { ModCtrl, 'f', search }, - { ModCtrl|ModShift, 'f', search }, - { ModCtrl, 'd', execute }, - { ModCtrl, 'o', open_file }, - { ModCtrl, 'p', pick_ctag }, - { ModCtrl, 'g', goto_ctag }, - { ModCtrl, 'n', new_win }, -}; - -/* External Commands - *****************************************************************************/ /* The shell: Filled in with $SHELL. Used to execute commands */ static char* ShellCmd[] = { NULL, "-c", NULL, NULL }; +static char* SedCmd[] = { "sed", "-e", NULL, NULL }; static char* PickFileCmd[] = { "xfilepick", ".", NULL }; static char* PickTagCmd[] = { "xtagpick", "tags", NULL, NULL }; static char* OpenCmd[] = { "xedit", NULL, NULL }; -static char* SedCmd[] = { "sed", "-e", NULL, NULL }; +static int SearchDir = DOWN; +static Tag Builtins[]; -/* Main Routine +/* Tag/Cmd Execution *****************************************************************************/ -#ifndef TEST -int main(int argc, char** argv) { - /* setup the shell */ - ShellCmd[0] = getenv("SHELL"); - if (!ShellCmd[0]) ShellCmd[0] = "/bin/sh"; - /* load the buffer views */ - char* tags = getenv("EDITTAGS"); - view_init(getview(TAGS), NULL); - view_putstr(getview(TAGS), (tags ? tags : DEFAULT_TAGS)); - view_selprev(getview(TAGS)); // clear the selection - buf_logclear(getbuf(TAGS)); - view_init(getview(EDIT), (argc > 1 ? argv[1] : NULL)); - /* initialize the display engine */ - x11_init(&Config); - x11_window("edit", Width, Height); - x11_show(); - Font = x11_font_load(FONTNAME); - x11_loop(); - return 0; +static Tag* tag_lookup(char* cmd) { + size_t len = 0; + Tag* tags = Builtins; + for (char* tag = cmd; *tag && !isspace(*tag); tag++, len++); + while (tags->tag) { + if (!strncmp(tags->tag, cmd, len)) + return tags; + tags++; + } + return NULL; } -#endif -static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y) { - enum RegionId id = getregion(x, y); - if (id != TAGS && id != EDIT) return; - if (Focused != id) Focused = id; - size_t row = (y-Regions[id].y) / x11_font_height(Font); - size_t col = (x-Regions[id].x) / x11_font_width(Font); - if (act == MOUSE_ACT_MOVE) { - if (MouseBtns[MOUSE_BTN_LEFT].pressed) { - if (MouseBtns[MOUSE_BTN_LEFT].count == 1) { - view_setcursor(getview(id), row, col); - MouseBtns[MOUSE_BTN_LEFT].count = 0; - } else { - view_selext(getview(id), row, col); - } - } +static void tag_exec(Tag* tag, char* arg) { + /* if we didnt get an arg, find one in the selection */ + if (!arg) arg = view_getstr(win_view(TAGS), NULL); + if (!arg) arg = view_getstr(win_view(EDIT), NULL); + /* execute the tag handler */ + tag->action.arg(arg); + free(arg); +} + +static void cmd_exec(char* cmd) { + char op = '\0'; + if (*cmd == ':' || *cmd == '!' || *cmd == '<' || *cmd == '|' || *cmd == '>') + op = *cmd, cmd++; + ShellCmd[2] = cmd; + /* execute the command */ + char *input = NULL, *output = NULL, *error = NULL; + WinRegion dest = EDIT; + if (op && op != '<' && op != '!' && 0 == view_selsize(win_view(EDIT))) + win_view(EDIT)->selection = (Sel){ .beg = 0, .end = buf_end(win_buf(EDIT)) }; + input = view_getstr(win_view(EDIT), NULL); + + if (op == '!') { + cmdrun(ShellCmd, NULL); + } else if (op == '>') { + dest = TAGS; + output = cmdwriteread(ShellCmd, input, &error); + } else if (op == '|') { + output = cmdwriteread(ShellCmd, input, &error); + } else if (op == ':') { + SedCmd[2] = cmd; + output = cmdwriteread(SedCmd, input, &error); } else { - MouseBtns[btn].pressed = (act == MOUSE_ACT_DOWN); - if (MouseBtns[btn].pressed) { - /* update the number of clicks and click time */ - uint32_t now = getmillis(); - uint32_t elapsed = now - MouseBtns[btn].time; - MouseBtns[btn].time = now; - MouseBtns[btn].region = id; - if (elapsed <= 250) - MouseBtns[btn].count++; - else - MouseBtns[btn].count = 1; - } else if (MouseBtns[btn].count > 0) { - /* execute the action on button release */ - if (MouseActs[btn]) - MouseActs[btn](id, MouseBtns[btn].count, row, col); - } + if (op != '<') dest = win_getregion(); + output = cmdread(ShellCmd, &error); } + + if (error) + view_append(win_view(TAGS), chomp(error)); + + if (output) { + if (op == '>') + view_append(win_view(dest), chomp(output)); + else + view_putstr(win_view(dest), output); + win_setregion(dest); + } + /* cleanup */ + free(input); + free(output); + free(error); } -static void key_handler(int mods, Rune key) { - /* handle the proper line endings */ - if (key == '\r') key = '\n'; - if (key == '\n' && currview()->buffer.crlf) key = RUNE_CRLF; - /* search for a key binding entry */ - uint32_t mkey = tolower(key); - for (int i = 0; i < nelem(Bindings); i++) { - int keymods = Bindings[i].mods; - if ((mkey == Bindings[i].key) && (keymods == ModAny || keymods == mods)) { - Bindings[i].action(); - return; - } +static void exec(char* cmd) { + /* skip leading space */ + for (; *cmd && isspace(*cmd); cmd++); + if (!*cmd) return; + /* see if it matches a builtin tag */ + Tag* tag = tag_lookup(cmd); + if (tag) { + while (*cmd && !isspace(*cmd++)); + tag_exec(tag, (*cmd ? stringdup(cmd) : NULL)); + } else { + cmd_exec(cmd); } - /* fallback to just inserting the rune if it doesn't fall in the private use area. - * the private use area is used to encode special keys */ - if (key < 0xE000 || key > 0xF8FF) - view_insert(currview(), true, key); } -/* Drawing Routines +/* Action Callbacks *****************************************************************************/ -#ifndef TEST -static void draw_runes(size_t x, size_t y, int fg, int bg, UGlyph* glyphs, size_t rlen) { - XGlyphSpec specs[rlen]; - while (rlen) { - size_t nspecs = x11_font_getglyphs(specs, (XGlyph*)glyphs, rlen, Font, x, y); - x11_draw_glyphs(fg, bg, specs, nspecs); - rlen -= nspecs; - } +static void delete(void) { + bool byword = x11_keymodsset(ModCtrl); + view_delete(win_view(FOCUSED), RIGHT, byword); } -static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols) { - XGlyphSpec specs[rlen]; - size_t i = 0; - while (rlen && i < ncols) { - int numspecs = 0; - uint32_t attr = glyphs[i].attr; - while (i < ncols && glyphs[i].attr == attr) { - x11_font_getglyph(Font, &(specs[numspecs]), glyphs[i].rune); - specs[numspecs].x = x; - specs[numspecs].y = y - x11_font_descent(Font); - x += x11_font_width(Font); - numspecs++; - i++; - /* skip over null chars which mark multi column runes */ - for (; i < ncols && !glyphs[i].rune; i++) - x += x11_font_width(Font); - } - /* Draw the glyphs with the proper colors */ - uint8_t bg = attr >> 8; - uint8_t fg = attr & 0xFF; - x11_draw_glyphs(fg, bg, specs, numspecs); - rlen -= numspecs; - } +static void onpaste(char* text) { + view_putstr(win_view(FOCUSED), text); } -static void draw_status(int fg, size_t ncols) { - Buf* buf = getbuf(EDIT); - UGlyph glyphs[ncols], *status = glyphs; - (status++)->rune = (buf->charset == BINARY ? 'B' : 'U'); - (status++)->rune = (buf->crlf ? 'C' : 'N'); - (status++)->rune = (buf->expand_tabs ? 'S' : 'T'); - (status++)->rune = (buf->copy_indent ? 'I' : 'i'); - (status++)->rune = (SearchDir < 0 ? '<' : '>'); - (status++)->rune = (buf->modified ? '*' : ' '); - (status++)->rune = ' '; - char* path = (buf->path ? buf->path : "*scratch*"); - size_t len = strlen(path); - if (len > ncols-4) { - (status++)->rune = '.'; - (status++)->rune = '.'; - (status++)->rune = '.'; - path += (len - ncols) + 6; - } - while (*path) - (status++)->rune = *path++; - draw_runes(2, 2, fg, 0, glyphs, status - glyphs); -} - -static void draw_region(enum RegionId id) { - size_t fheight = x11_font_height(Font); - size_t fwidth = x11_font_width(Font); - /* update the screen buffer and retrieve cursor coordinates */ - View* view = getview(id); - size_t csrx = SIZE_MAX, csry = SIZE_MAX; - view_update(view, &csrx, &csry); - /* draw the region to the frame buffer */ - if (id == TAGS) - x11_draw_rect(CLR_BASE02, Regions[id].x-2, Regions[id].y-2, Regions[id].width+4, Regions[id].height+4); - x11_draw_rect(CLR_BASE01, 0, Regions[id].y - 3, Regions[id].width + 4, 1); - for (size_t y = 0; y < view->nrows; y++) { - Row* row = view_getrow(view, y); - draw_glyphs(2, Regions[id].y + ((y+1) * fheight), row->cols, row->rlen, row->len); +static void cut(void) { + char* str = view_getstr(win_view(FOCUSED), NULL); + x11_setsel(CLIPBOARD, str); + if (str && *str) delete(); +} + +static void paste(void) { + assert(x11_getsel(CLIPBOARD, onpaste)); +} + +static void copy(void) { + char* str = view_getstr(win_view(FOCUSED), NULL); + x11_setsel(CLIPBOARD, str); +} + +static void quit(void) { + static uint64_t before = 0; + uint64_t now = getmillis(); + if (!win_buf(EDIT)->modified || (now-before) <= 250) { + x11_deinit(); + } else { + view_append(win_view(TAGS), + "File is modified. Repeat action twice in < 250ms to quit."); } - /* Place cursor on screen */ - if (id == Focused && csrx != SIZE_MAX && csry != SIZE_MAX) - x11_draw_rect(CLR_BASE3, 2 + csrx * fwidth, Regions[id].y + (csry * fheight), 1, fheight); - - if (Regions[id].warp_ptr) { - Regions[id].warp_ptr = false; - size_t x = 2 + (csrx * fwidth) - (fwidth/2); - size_t y = Regions[id].y + (csry * fheight) + (fheight/2); - x11_warp_mouse(x,y); + before = now; +} + +static void save(void) { + buf_save(win_buf(EDIT)); +} + +/* Mouse Handling + *****************************************************************************/ +void mouse_left(WinRegion id, size_t count, size_t row, size_t col) { + if (count == 1) { + if (x11_keymodsset(ModShift)) + view_selext(win_view(id), row, col); + else + view_setcursor(win_view(id), row, col); + } else if (count == 2) { + view_select(win_view(id), row, col); + } else if (count == 3) { + view_selword(win_view(id), row, col); } } -static void layout(int width, int height) { - /* initialize all of the regions to overlap the status region */ - size_t fheight = x11_font_height(Font); - size_t fwidth = x11_font_width(Font); - for (int i = 0; i < NREGIONS; i++) { - Regions[i].x = 2; - Regions[i].y = 2; - Regions[i].width = (width - 4); - Regions[i].height = fheight; +void mouse_middle(WinRegion id, size_t count, size_t row, size_t col) { + if (win_btnpressed(MOUSE_BTN_LEFT)) { + cut(); + } else { + char* str = view_fetchcmd(win_view(id), row, col); + if (str) exec(str); + free(str); } - /* Place the tag region relative to status */ - Regions[TAGS].y = 5 + Regions[STATUS].y + Regions[STATUS].height; - size_t maxtagrows = ((height - Regions[TAGS].y - 5) / 4) / fheight; - size_t tagcols = Regions[TAGS].width / fwidth; - size_t tagrows = view_limitrows(getview(TAGS), maxtagrows, tagcols); - Regions[TAGS].height = tagrows * fheight; - view_resize(getview(TAGS), tagrows, tagcols); - /* Place the edit region relative to status */ - Regions[EDIT].y = 5 + Regions[TAGS].y + Regions[TAGS].height; - Regions[EDIT].height = (height - Regions[EDIT].y - 5); - view_resize(getview(EDIT), Regions[EDIT].height / fheight, Regions[EDIT].width / fwidth); -} - -static void redraw(int width, int height) { - size_t fheight = x11_font_height(Font); - size_t fwidth = x11_font_width(Font); - /* if the window is too small, don't bother updating. */ - if (width < fwidth || height < (4 * fheight)) - return; - layout(width, height); - x11_draw_rect(CLR_BASE03, 0, 0, width, height); - x11_draw_rect(CLR_BASE02, (79 * fwidth) + 2, Regions[EDIT].y-2, fwidth, height - Regions[EDIT].y + 2); - draw_status(CLR_BASE1, (width - 4) / x11_font_width(Font)); - draw_region(TAGS); - draw_region(EDIT); - cmdreap(); // cleanup any zombie child processes } -#endif -/* UI Callbacks - *****************************************************************************/ -static void delete(void) { - bool byword = x11_keymodsset(ModCtrl); - view_delete(currview(), RIGHT, byword); +void mouse_right(WinRegion id, size_t count, size_t row, size_t col) { + if (win_btnpressed(MOUSE_BTN_LEFT)) { + paste(); + } else { + SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1); + view_find(win_view(id), SearchDir, row, col); + //Regions[id].warp_ptr = true; + } } +/* Keyboard Handling */ static void del_to_bol(void) { - view_bol(currview(), true); - if (view_selsize(currview()) > 0) + view_bol(win_view(FOCUSED), true); + if (view_selsize(win_view(FOCUSED)) > 0) delete(); } static void del_to_eol(void) { - view_eol(currview(), true); - if (view_selsize(currview()) > 0) + view_eol(win_view(FOCUSED), true); + if (view_selsize(win_view(FOCUSED)) > 0) delete(); } static void del_to_bow(void) { - view_byword(currview(), LEFT, true); - if (view_selsize(currview()) > 0) + view_byword(win_view(FOCUSED), LEFT, true); + if (view_selsize(win_view(FOCUSED)) > 0) delete(); } static void backspace(void) { bool byword = x11_keymodsset(ModCtrl); - view_delete(currview(), LEFT, byword); + view_delete(win_view(FOCUSED), LEFT, byword); } static void cursor_bol(void) { - view_bol(currview(), false); + view_bol(win_view(FOCUSED), false); } static void cursor_eol(void) { - view_eol(currview(), false); + view_eol(win_view(FOCUSED), false); } static void cursor_home(void) { bool extsel = x11_keymodsset(ModShift); if (x11_keymodsset(ModCtrl)) - view_bof(currview(), extsel); + view_bof(win_view(FOCUSED), extsel); else - view_bol(currview(), extsel); + view_bol(win_view(FOCUSED), extsel); } static void cursor_end(void) { bool extsel = x11_keymodsset(ModShift); if (x11_keymodsset(ModCtrl)) - view_eof(currview(), extsel); + view_eof(win_view(FOCUSED), extsel); else - view_eol(currview(), extsel); + view_eol(win_view(FOCUSED), extsel); } static void cursor_up(void) { bool extsel = x11_keymodsset(ModShift); - view_byline(currview(), UP, extsel); + view_byline(win_view(FOCUSED), UP, extsel); } static void cursor_dn(void) { bool extsel = x11_keymodsset(ModShift); - view_byline(currview(), DOWN, extsel); + view_byline(win_view(FOCUSED), DOWN, extsel); } static void cursor_left(void) { bool extsel = x11_keymodsset(ModShift); if (x11_keymodsset(ModCtrl)) - view_byword(currview(), LEFT, extsel); + view_byword(win_view(FOCUSED), LEFT, extsel); else - view_byrune(currview(), LEFT, extsel); + view_byrune(win_view(FOCUSED), LEFT, extsel); } static void cursor_right(void) { bool extsel = x11_keymodsset(ModShift); if (x11_keymodsset(ModCtrl)) - view_byword(currview(), RIGHT, extsel); + view_byword(win_view(FOCUSED), RIGHT, extsel); else - view_byrune(currview(), RIGHT, extsel); + view_byrune(win_view(FOCUSED), RIGHT, extsel); } static void page_up(void) { - view_scrollpage(currview(), UP); + view_scrollpage(win_view(FOCUSED), UP); } static void page_dn(void) { - view_scrollpage(currview(), DOWN); + view_scrollpage(win_view(FOCUSED), DOWN); } static void select_prev(void) { - view_selprev(currview()); + view_selprev(win_view(FOCUSED)); } static void change_focus(void) { - Focused = (Focused == TAGS ? EDIT : TAGS); -} - -static void quit(void) { - static uint64_t before = 0; - uint64_t now = getmillis(); - if (!getbuf(EDIT)->modified || (now-before) <= 250) { - x11_deinit(); - } else { - view_append(getview(TAGS), - "File is modified. Repeat action twice in < 250ms to quit."); - } - before = now; -} - -static void save(void) { - buf_save(getbuf(EDIT)); + win_setregion(win_getregion() == TAGS ? EDIT : TAGS); } static void undo(void) { - view_undo(currview()); + view_undo(win_view(FOCUSED)); } static void redo(void) { - view_redo(currview()); + view_redo(win_view(FOCUSED)); } static void tag_undo(void) { - view_undo(getview(EDIT)); + view_undo(win_view(EDIT)); } static void tag_redo(void) { - view_redo(getview(EDIT)); -} - -static void cut(void) { - char* str = view_getstr(currview(), NULL); - x11_setsel(CLIPBOARD, str); - if (str && *str) delete(); -} - -static void copy(void) { - char* str = view_getstr(currview(), NULL); - x11_setsel(CLIPBOARD, str); -} - -static void onpaste(char* text) { - view_putstr(currview(), text); -} - -static void paste(void) { - assert(x11_getsel(CLIPBOARD, onpaste)); + view_redo(win_view(EDIT)); } static void search(void) { SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1); - char* str = view_getctx(currview()); - view_findstr(getview(EDIT), SearchDir, str); + char* str = view_getctx(win_view(FOCUSED)); + view_findstr(win_view(EDIT), SearchDir, str); free(str); - Regions[EDIT].warp_ptr = true; + //Regions[EDIT].warp_ptr = true; } static void execute(void) { - char* str = view_getcmd(currview()); + char* str = view_getcmd(win_view(FOCUSED)); if (str) exec(str); free(str); } static void find(char* arg) { SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1); - view_findstr(getview(EDIT), SearchDir, arg); + view_findstr(win_view(EDIT), SearchDir, arg); } static void open_file(void) { char* file = cmdread(PickFileCmd, NULL); if (file) { file = chomp(file); - if (!getbuf(EDIT)->path && !getbuf(EDIT)->modified) { - buf_load(getbuf(EDIT), file); + if (!win_buf(EDIT)->path && !win_buf(EDIT)->modified) { + buf_load(win_buf(EDIT), file); } else { OpenCmd[1] = file; cmdrun(OpenCmd, NULL); @@ -574,13 +318,13 @@ static void pick_symbol(char* symbol) { PickTagCmd[2] = symbol; char* pick = cmdread(PickTagCmd, NULL); if (pick) { - Buf* buf = getbuf(EDIT); + Buf* buf = win_buf(EDIT); if (buf->path && 0 == strncmp(buf->path, pick, strlen(buf->path))) { - view_setln(getview(EDIT), strtoul(strrchr(pick, ':')+1, NULL, 0)); - Focused = EDIT; + view_setln(win_view(EDIT), strtoul(strrchr(pick, ':')+1, NULL, 0)); + win_setregion(EDIT); } else { if (!buf->path && !buf->modified) { - view_init(getview(EDIT), pick); + view_init(win_view(EDIT), pick); } else { OpenCmd[1] = chomp(pick); cmdrun(OpenCmd, NULL); @@ -594,12 +338,12 @@ static void pick_ctag(void) { } static void goto_ctag(void) { - char* str = view_getctx(currview()); + char* str = view_getctx(win_view(FOCUSED)); if (str) { size_t line = strtoul(str, NULL, 0); if (line) { - view_setln(getview(EDIT), line); - Focused = EDIT; + view_setln(win_view(EDIT), line); + win_setregion(EDIT); } else { pick_symbol(str); } @@ -608,224 +352,141 @@ static void goto_ctag(void) { } static void tabs(void) { - bool enabled = !(getbuf(EDIT)->expand_tabs); - getbuf(EDIT)->expand_tabs = enabled; - getbuf(TAGS)->expand_tabs = enabled; + bool enabled = !(win_buf(EDIT)->expand_tabs); + win_buf(EDIT)->expand_tabs = enabled; + win_buf(TAGS)->expand_tabs = enabled; } static void indent(void) { - bool enabled = !(getbuf(EDIT)->copy_indent); - getbuf(EDIT)->copy_indent = enabled; - getbuf(TAGS)->copy_indent = enabled; + bool enabled = !(win_buf(EDIT)->copy_indent); + win_buf(EDIT)->copy_indent = enabled; + win_buf(TAGS)->copy_indent = enabled; } static void del_indent(void) { - view_indent(currview(), LEFT); + view_indent(win_view(FOCUSED), LEFT); } static void add_indent(void) { - view_indent(currview(), RIGHT); + view_indent(win_view(FOCUSED), RIGHT); } static void eol_mode(void) { - int crlf = getbuf(EDIT)->crlf; - getbuf(EDIT)->crlf = !crlf; - getbuf(TAGS)->crlf = !crlf; + int crlf = win_buf(EDIT)->crlf; + win_buf(EDIT)->crlf = !crlf; + win_buf(TAGS)->crlf = !crlf; exec(crlf ? "|dos2unix" : "|unix2dos"); } -static void dump_log(Log* log) { - for (; log != NULL; log = log->next) { - if (log->insert) { - printf(" INS %d %lu %lu\n", - log->transid, log->data.ins.beg, log->data.ins.end); - } else { - printf(" DEL %d %lu %lu\n", - log->transid, log->data.del.off, log->data.del.len); - } - } -} - -static void debug_dump(void) { - Buf* buf = currbuf(); - Log* log; - printf("path: '%s'\n", buf->path); - printf("charset: %d\n", buf->charset); - printf("crlf: %d\n", buf->crlf); - printf("bufsize: %lu\n", buf->bufsize); - printf("modified: %d\n", buf->modified); - printf("tab_mode: %d\n", buf->expand_tabs); - printf("indent: %d\n", buf->copy_indent); - printf("bufstart: %p\n", (void*)buf->bufstart); - printf("bufend: %p\n", (void*)buf->bufend); - printf("gapstart: %p\n", (void*)buf->gapstart); - printf("gapend: %p\n", (void*)buf->gapend); - printf("undo:\n"); - dump_log(buf->undo); - printf("redo:\n"); - dump_log(buf->redo); -} - static void new_win(void) { cmd_exec("!edit"); } -/* Tag/Cmd Execution +/* Main Routine *****************************************************************************/ -static Tag* tag_lookup(char* cmd) { - size_t len = 0; - Tag* tags = Builtins; - for (char* tag = cmd; *tag && !isspace(*tag); tag++, len++); - while (tags->tag) { - if (!strncmp(tags->tag, cmd, len)) - return tags; - tags++; - } - return NULL; -} - -static void tag_exec(Tag* tag, char* arg) { - /* if we didnt get an arg, find one in the selection */ - if (!arg) arg = view_getstr(getview(TAGS), NULL); - if (!arg) arg = view_getstr(getview(EDIT), NULL); - /* execute the tag handler */ - tag->action.arg(arg); - free(arg); -} - -static void cmd_exec(char* cmd) { - char op = '\0'; - if (*cmd == ':' || *cmd == '!' || *cmd == '<' || *cmd == '|' || *cmd == '>') - op = *cmd, cmd++; - ShellCmd[2] = cmd; - /* execute the command */ - char *input = NULL, *output = NULL, *error = NULL; - enum RegionId dest = EDIT; - if (op && op != '<' && op != '!' && 0 == view_selsize(getview(EDIT))) - getview(EDIT)->selection = (Sel){ .beg = 0, .end = buf_end(getbuf(EDIT)) }; - input = view_getstr(getview(EDIT), NULL); +static Tag Builtins[] = { + { .tag = "Quit", .action.noarg = quit }, + { .tag = "Save", .action.noarg = save }, + { .tag = "Cut", .action.noarg = cut }, + { .tag = "Copy", .action.noarg = copy }, + { .tag = "Paste", .action.noarg = paste }, + { .tag = "Undo", .action.noarg = tag_undo }, + { .tag = "Redo", .action.noarg = tag_redo }, + { .tag = "Find", .action.arg = find }, + { .tag = "Tabs", .action.noarg = tabs }, + { .tag = "Indent", .action.noarg = indent }, + { .tag = "Eol", .action.noarg = eol_mode }, + { .tag = NULL, .action.noarg = NULL } +}; - if (op == '!') { - cmdrun(ShellCmd, NULL); - } else if (op == '>') { - dest = TAGS; - output = cmdwriteread(ShellCmd, input, &error); - } else if (op == '|') { - output = cmdwriteread(ShellCmd, input, &error); - } else if (op == ':') { - SedCmd[2] = cmd; - output = cmdwriteread(SedCmd, input, &error); - } else { - if (op != '<') dest = Focused; - output = cmdread(ShellCmd, &error); - } - - if (error) - view_append(getview(TAGS), chomp(error)); +static KeyBinding Bindings[] = { + /* Cursor Movements */ + { ModAny, KEY_HOME, cursor_home }, + { ModAny, KEY_END, cursor_end }, + { ModAny, KEY_UP, cursor_up }, + { ModAny, KEY_DOWN, cursor_dn }, + { ModAny, KEY_LEFT, cursor_left }, + { ModAny, KEY_RIGHT, cursor_right }, - if (output) { - if (op == '>') - view_append(getview(dest), chomp(output)); - else - view_putstr(getview(dest), output); - Focused = dest; - } - /* cleanup */ - free(input); - free(output); - free(error); -} - -static void exec(char* cmd) { - /* skip leading space */ - for (; *cmd && isspace(*cmd); cmd++); - if (!*cmd) return; - /* see if it matches a builtin tag */ - Tag* tag = tag_lookup(cmd); - if (tag) { - while (*cmd && !isspace(*cmd++)); - tag_exec(tag, (*cmd ? stringdup(cmd) : NULL)); - } else { - cmd_exec(cmd); - } -} + /* Standard Unix Shortcuts */ + { ModCtrl, 'u', del_to_bol }, + { ModCtrl, 'k', del_to_eol }, + { ModCtrl, 'w', del_to_bow }, + { ModCtrl, 'h', backspace }, + { ModCtrl, 'a', cursor_bol }, + { ModCtrl, 'e', cursor_eol }, -/* Mouse Handling - *****************************************************************************/ -static void mouse_left(enum RegionId id, size_t count, size_t row, size_t col) { - if (count == 1) { - if (x11_keymodsset(ModShift)) - view_selext(getview(id), row, col); - else - view_setcursor(getview(id), row, col); - } else if (count == 2) { - view_select(getview(id), row, col); - } else if (count == 3) { - view_selword(getview(id), row, col); - } -} + /* Standard Text Editing Shortcuts */ + { ModCtrl, 's', save }, + { ModCtrl, 'z', undo }, + { ModCtrl, 'y', redo }, + { ModCtrl, 'x', cut }, + { ModCtrl, 'c', copy }, + { ModCtrl, 'v', paste }, + + /* Block Indent */ + { ModCtrl, '[', del_indent }, + { ModCtrl, ']', add_indent }, -static void mouse_middle(enum RegionId id, size_t count, size_t row, size_t col) { - if (MouseBtns[MOUSE_BTN_LEFT].pressed) { - cut(); - } else { - char* str = view_fetchcmd(getview(id), row, col); - if (str) exec(str); - free(str); - } -} + /* Common Special Keys */ + { ModNone, KEY_PGUP, page_up }, + { ModNone, KEY_PGDN, page_dn }, + { ModAny, KEY_DELETE, delete }, + { ModAny, KEY_BACKSPACE, backspace }, -static void mouse_right(enum RegionId id, size_t count, size_t row, size_t col) { - if (MouseBtns[MOUSE_BTN_LEFT].pressed) { - paste(); - } else { - SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1); - view_find(getview(id), SearchDir, row, col); - Regions[id].warp_ptr = true; - } -} + /* Implementation Specific */ + { ModNone, KEY_ESCAPE, select_prev }, + { ModCtrl, 't', change_focus }, + { ModCtrl, 'q', quit }, + { ModCtrl, 'f', search }, + { ModCtrl|ModShift, 'f', search }, + { ModCtrl, 'd', execute }, + { ModCtrl, 'o', open_file }, + { ModCtrl, 'p', pick_ctag }, + { ModCtrl, 'g', goto_ctag }, + { ModCtrl, 'n', new_win }, + { 0, 0, 0 } +}; -static void mouse_wheelup(enum RegionId id, size_t count, size_t row, size_t col) { - view_scroll(getview(id), -ScrollLines); +void onupdate(void) { + static char status_bytes[256]; + memset(status_bytes, 0, sizeof(status_bytes)); + char* status = status_bytes; + Buf* buf = win_buf(EDIT); + *(status++) = (buf->charset == BINARY ? 'B' : 'U'); + *(status++) = (buf->crlf ? 'C' : 'N'); + *(status++) = (buf->expand_tabs ? 'S' : 'T'); + *(status++) = (buf->copy_indent ? 'I' : 'i'); + *(status++) = (SearchDir < 0 ? '<' : '>'); + *(status++) = (buf->modified ? '*' : ' '); + *(status++) = ' '; + char* path = (buf->path ? buf->path : "*scratch*"); + size_t remlen = sizeof(status_bytes) - strlen(status_bytes) - 1; + strncat(status, path, remlen); + win_settext(STATUS, status_bytes); } -static void mouse_wheeldn(enum RegionId id, size_t count, size_t row, size_t col) { - view_scroll(getview(id), +ScrollLines); +#ifndef TEST +int main(int argc, char** argv) { + /* setup the shell */ + ShellCmd[0] = getenv("SHELL"); + if (!ShellCmd[0]) ShellCmd[0] = "/bin/sh"; + /* Create the window and enter the event loop */ + win_init("edit"); + char* tags = getenv("EDITTAGS"); + win_settext(TAGS, (tags ? tags : DEFAULT_TAGS)); + view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL)); + win_setkeys(Bindings); + //win_setmouse(&MouseHandlers); + win_loop(); + return 0; } +#endif -/* Region Utils - *****************************************************************************/ -static View* getview(enum RegionId id) { - return &(Regions[id].view); -} -static Buf* getbuf(enum RegionId id) { - return &(getview(id)->buffer); -} -static View* currview(void) { - return getview(Focused); -} -static Buf* currbuf(void) { - return getbuf(Focused); -} -static enum RegionId getregion(size_t x, size_t y) { - for (int i = 0; i < NREGIONS; i++) { - size_t startx = Regions[i].x, endx = startx + Regions[i].width; - size_t starty = Regions[i].y, endy = starty + Regions[i].height; - if ((startx <= x && x <= endx) && (starty <= y && y <= endy)) - return (enum RegionId)i; - } - return NREGIONS; -} -static Sel* getsel(enum RegionId id) { - return &(getview(id)->selection); -} -static Sel* currsel(void) { - return getsel(Focused); -}