diff options
Diffstat (limited to 'src/engine/textedit.h')
| -rw-r--r-- | src/engine/textedit.h | 337 |
1 files changed, 97 insertions, 240 deletions
diff --git a/src/engine/textedit.h b/src/engine/textedit.h index 0dcda1d..742fd33 100644 --- a/src/engine/textedit.h +++ b/src/engine/textedit.h @@ -1,31 +1,21 @@ -struct editline -{ +struct editline { enum { CHUNKSIZE = 256 }; - char *text; int len, maxlen; - editline() : text(NULL), len(0), maxlen(0) {} - editline(const char *init) : text(NULL), len(0), maxlen(0) - { + editline(const char *init) : text(NULL), len(0), maxlen(0) { set(init); } - bool empty() { return len <= 0; } - - void clear() - { + void clear() { DELETEA(text); len = maxlen = 0; } - - bool grow(int total, const char *fmt = "", ...) - { + bool grow(int total, const char *fmt = "", ...) { if(total + 1 <= maxlen) return false; maxlen = (total + CHUNKSIZE) - total%CHUNKSIZE; char *newtext = new char[maxlen]; - if(fmt) - { + if(fmt) { va_list args; va_start(args, fmt); vformatstring(newtext, fmt, args, maxlen); @@ -36,69 +26,52 @@ struct editline text = newtext; return true; } - - void set(const char *str, int slen = -1) - { - if(slen < 0) - { + void set(const char *str, int slen = -1) { + if(slen < 0) { slen = strlen(str); if(!grow(slen, "%s", str)) memcpy(text, str, slen + 1); } - else - { + else { grow(slen); memcpy(text, str, slen); text[slen] = '\0'; } len = slen; } - - void prepend(const char *str) - { + void prepend(const char *str) { int slen = strlen(str); - if(!grow(slen + len, "%s%s", str, text ? text : "")) - { + if(!grow(slen + len, "%s%s", str, text ? text : "")) { memmove(&text[slen], text, len + 1); memcpy(text, str, slen + 1); } len += slen; } - - void append(const char *str) - { + void append(const char *str) { int slen = strlen(str); if(!grow(len + slen, "%s%s", text ? text : "", str)) memcpy(&text[len], str, slen + 1); len += slen; } - - bool read(stream *f, int chop = -1) - { + bool read(stream *f, int chop = -1) { if(chop < 0) chop = INT_MAX; else chop++; set(""); - while(len + 1 < chop && f->getline(&text[len], min(maxlen, chop) - len)) - { + while(len + 1 < chop && f->getline(&text[len], min(maxlen, chop) - len)) { len += strlen(&text[len]); - if(len > 0 && text[len-1] == '\n') - { + if(len > 0 && text[len-1] == '\n') { text[--len] = '\0'; return true; } if(len + 1 >= maxlen && len + 1 < chop) grow(len + CHUNKSIZE, "%s", text); } - if(len + 1 >= chop) - { + if(len + 1 >= chop) { char buf[CHUNKSIZE]; - while(f->getline(buf, sizeof(buf))) - { + while(f->getline(buf, sizeof(buf))) { int blen = strlen(buf); if(blen > 0 && buf[blen-1] == '\n') return true; } } return len > 0; } - - void del(int start, int count) - { + void del(int start, int count) { if(!text) return; if(start < 0) { count += start; start = 0; } if(count <= 0 || start >= len) return; @@ -106,16 +79,12 @@ struct editline memmove(&text[start], &text[start+count], len + 1 - (start + count)); len -= count; } - - void chop(int newlen) - { + void chop(int newlen) { if(!text) return; len = clamp(newlen, 0, len); text[len] = '\0'; } - - void insert(char *str, int start, int count = 0) - { + void insert(char *str, int start, int count = 0) { if(count <= 0) count = strlen(str); start = clamp(start, 0, len); grow(len + count, "%s", text ? text : ""); @@ -123,12 +92,9 @@ struct editline memcpy(&text[start], str, count); len += count; } - - void combinelines(vector<editline> &src) - { + void combinelines(vector<editline> &src) { if(src.empty()) set(""); - else loopv(src) - { + else loopv(src) { if(i) append("\n"); if(!i) set(src[i].text, src[i].len); else insert(src[i].text, len, src[i].len); @@ -136,102 +102,77 @@ struct editline } }; -struct editor -{ +struct editor { int mode; //editor mode - 1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes) bool active, rendered; const char *name; const char *filename; - int cx, cy; // cursor position - ensured to be valid after a region() or currentline() int mx, my; // selection mark, mx=-1 if following cursor - avoid direct access, instead use region() int maxx, maxy; // maxy=-1 if unlimited lines, 1 if single line editor - int scrolly; // vertical scroll offset - bool linewrap; int pixelwidth; // required for up/down/hit/draw/bounds int pixelheight; // -1 for variable sized, i.e. from bounds() - vector<editline> lines; // MUST always contain at least one line! - editor(const char *name, int mode, const char *initval) : mode(mode), active(true), rendered(false), name(newstring(name)), filename(NULL), - cx(0), cy(0), mx(-1), maxx(-1), maxy(-1), scrolly(0), linewrap(false), pixelwidth(-1), pixelheight(-1) - { + cx(0), cy(0), mx(-1), maxx(-1), maxy(-1), scrolly(0), linewrap(false), pixelwidth(-1), pixelheight(-1) { //printf("editor %08x '%s'\n", this, name); lines.add().set(initval ? initval : ""); } - - ~editor() - { + ~editor() { //printf("~editor %08x '%s'\n", this, name); DELETEA(name); DELETEA(filename); clear(NULL); } - - void clear(const char *init = "") - { + void clear(const char *init = "") { cx = cy = 0; mark(false); loopv(lines) lines[i].clear(); lines.shrink(0); if(init) lines.add().set(init); } - - void setfile(const char *fname) - { + void setfile(const char *fname) { DELETEA(filename); if(fname) filename = newstring(fname); } - - void load() - { + void load() { if(!filename) return; clear(NULL); stream *file = openutf8file(filename, "r"); - if(file) - { + if(file) { while(lines.add().read(file, maxx) && (maxy < 0 || lines.length() <= maxy)); lines.pop().clear(); delete file; } if(lines.empty()) lines.add().set(""); } - - void save() - { + void save() { if(!filename) return; stream *file = openutf8file(filename, "w"); if(!file) return; loopv(lines) file->putline(lines[i].text); delete file; } - - void mark(bool enable) - { + void mark(bool enable) { mx = (enable) ? cx : -1; my = cy; } - - void selectall() - { + void selectall() { mx = my = INT_MAX; cx = cy = 0; } - // constrain results to within buffer - s=start, e=end, return true if a selection range // also ensures that cy is always within lines[] and cx is valid - bool region(int &sx, int &sy, int &ex, int &ey) - { + bool region(int &sx, int &sy, int &ex, int &ey) { int n = lines.length(); ASSERT(n != 0); if(cy < 0) cy = 0; else if(cy >= n) cy = n-1; int len = lines[cy].len; if(cx < 0) cx = 0; else if(cx > len) cx = len; - if(mx >= 0) - { + if(mx >= 0) { if(my < 0) my = 0; else if(my >= n) my = n-1; len = lines[my].len; if(mx > len) mx = len; @@ -244,34 +185,26 @@ struct editor else if(sy==ey && sx > ex) swap(sx, ex); return (sx != ex) || (sy != ey); } - bool region() { int sx, sy, ex, ey; return region(sx, sy, ex, ey); } - // also ensures that cy is always within lines[] and cx is valid - editline ¤tline() - { + editline ¤tline() { int n = lines.length(); ASSERT(n != 0); if(cy < 0) cy = 0; else if(cy >= n) cy = n-1; if(cx < 0) cx = 0; else if(cx > lines[cy].len) cx = lines[cy].len; return lines[cy]; } - - void copyselectionto(editor *b) - { + void copyselectionto(editor *b) { if(b==this) return; - b->clear(NULL); int sx, sy, ex, ey; region(sx, sy, ex, ey); - loopi(1+ey-sy) - { + loopi(1+ey-sy) { if(b->maxy != -1 && b->lines.length() >= b->maxy) break; int y = sy+i; char *line = lines[y].text; int len = lines[y].len; - if(y == sy && y == ey) - { + if(y == sy && y == ey) { line += sx; len = ex - sx; } @@ -281,15 +214,12 @@ struct editor } if(b->lines.empty()) b->lines.add().set(""); } - - char *tostring() - { + char *tostring() { int len = 0; loopv(lines) len += lines[i].len + 1; char *str = newstring(len); int offset = 0; - loopv(lines) - { + loopv(lines) { editline &l = lines[i]; memcpy(&str[offset], l.text, l.len); offset += l.len; @@ -298,19 +228,15 @@ struct editor str[offset] = '\0'; return str; } - - char *selectiontostring() - { + char *selectiontostring() { vector<char> buf; int sx, sy, ex, ey; region(sx, sy, ex, ey); - loopi(1+ey-sy) - { + loopi(1+ey-sy) { int y = sy+i; char *line = lines[y].text; int len = lines[y].len; - if(y == sy && y == ey) - { + if(y == sy && y == ey) { line += sx; len = ex - sx; } @@ -322,28 +248,21 @@ struct editor buf.add('\0'); return newstring(buf.getbuf(), buf.length()-1); } - - void removelines(int start, int count) - { + void removelines(int start, int count) { loopi(count) lines[start+i].clear(); lines.remove(start, count); } - - bool del() // removes the current selection (if any) - { + bool del() { // removes the current selection (if any) { int sx, sy, ex, ey; - if(!region(sx, sy, ex, ey)) - { + if(!region(sx, sy, ex, ey)) { mark(false); return false; } - if(sy == ey) - { + if(sy == ey) { if(sx == 0 && ex == lines[ey].len) removelines(sy, 1); else lines[sy].del(sx, ex - sx); } - else - { + else { if(ey > sy+1) { removelines(sy+1, ey-(sy+1)); ey = sy+1; } if(ex == lines[ey].len) removelines(ey, 1); else lines[ey].del(0, ex); if(sx == 0) removelines(sy, 1); else lines[sy].del(sx, lines[sy].len - sx); @@ -353,22 +272,17 @@ struct editor cx = sx; cy = sy; editline ¤t = currentline(); - if(cx >= current.len && cy < lines.length() - 1) - { + if(cx >= current.len && cy < lines.length() - 1) { current.append(lines[cy+1].text); removelines(cy + 1, 1); } return true; } - - void insert(char ch) - { + void insert(char ch) { del(); editline ¤t = currentline(); - if(ch == '\n') - { - if(maxy == -1 || cy < maxy-1) - { + if(ch == '\n') { + if(maxy == -1 || cy < maxy-1) { editline newline(¤t.text[cx]); current.chop(cx); cy = min(lines.length(), cy+1); @@ -377,49 +291,36 @@ struct editor else current.chop(cx); cx = 0; } - else - { + else { int len = current.len; if(maxx >= 0 && len > maxx-1) len = maxx-1; if(cx <= len) current.insert(&ch, cx++, 1); } } - - void insert(const char *s) - { + void insert(const char *s) { while(*s) insert(*s++); } - - void insertallfrom(editor *b) - { + void insertallfrom(editor *b) { if(b==this) return; - del(); - - if(b->lines.length() == 1 || maxy == 1) - { + if(b->lines.length() == 1 || maxy == 1) { editline ¤t = currentline(); char *str = b->lines[0].text; int slen = b->lines[0].len; if(maxx >= 0 && b->lines[0].len + cx > maxx) slen = maxx-cx; - if(slen > 0) - { + if(slen > 0) { int len = current.len; if(maxx >= 0 && slen + cx + len > maxx) len = max(0, maxx-(cx+slen)); current.insert(str, cx, slen); cx += slen; } } - else - { - loopv(b->lines) - { - if(!i) - { + else { + loopv(b->lines) { + if(!i) { lines[cy++].append(b->lines[i].text); } - else if(i >= b->lines.length()) - { + else if(i >= b->lines.length()) { cx = b->lines[i].len; lines[cy].prepend(b->lines[i].text); } @@ -427,14 +328,10 @@ struct editor } } } - - void key(int code) - { - switch(code) - { + void key(int code) { + switch(code) { case SDLK_UP: - if(linewrap) - { + if(linewrap) { int x, y; char *str = currentline().text; text_pos(str, cx+1, x, y, pixelwidth); @@ -443,8 +340,7 @@ struct editor cy--; break; case SDLK_DOWN: - if(linewrap) - { + if(linewrap) { int x, y, width, height; char *str = currentline().text; text_pos(str, cx, x, y, pixelwidth); @@ -479,24 +375,22 @@ struct editor cx++; break; case SDLK_DELETE: - if(!del()) - { + if(!del()) { editline ¤t = currentline(); if(cx < current.len) current.del(cx, 1); - else if(cy < lines.length()-1) - { //combine with next line + else if(cy < lines.length()-1) { + //combine with next line current.append(lines[cy+1].text); removelines(cy+1, 1); } } break; case SDLK_BACKSPACE: - if(!del()) - { + if(!del()) { editline ¤t = currentline(); if(cx > 0) current.del(--cx, 1); - else if(cy > 0) - { //combine with previous line + else if(cy > 0) { + //combine with previous line cx = lines[cy-1].len; lines[cy-1].append(current.text); removelines(cy--, 1); @@ -514,24 +408,17 @@ struct editor break; } } - - void input(const char *str, int len) - { + void input(const char *str, int len) { loopi(len) insert(str[i]); } - - void hit(int hitx, int hity, bool dragged) - { + void hit(int hitx, int hity, bool dragged) { int maxwidth = linewrap?pixelwidth:-1; int h = 0; - for(int i = scrolly; i < lines.length(); i++) - { + for(int i = scrolly; i < lines.length(); i++) { int width, height; text_bounds(lines[i].text, width, height, maxwidth); if(h + height > pixelheight) break; - - if(hity >= h && hity <= h+height) - { + if(hity >= h && hity <= h+height) { int x = text_visible(lines[i].text, hitx, hity-h, maxwidth); if(dragged) { mx = x; my = i; } else { cx = x; cy = i; }; break; @@ -539,13 +426,10 @@ struct editor h+=height; } } - - int limitscrolly() - { + int limitscrolly() { int maxwidth = linewrap?pixelwidth:-1; int slines = lines.length(); - for(int ph = pixelheight; slines > 0 && ph > 0;) - { + for(int ph = pixelheight; slines > 0 && ph > 0;) { int width, height; text_bounds(lines[slines-1].text, width, height, maxwidth); if(height > ph) break; @@ -554,39 +438,30 @@ struct editor } return slines; } - - void draw(int x, int y, int color, bool hit) - { + void draw(int x, int y, int color, bool hit) { int maxwidth = linewrap?pixelwidth:-1; - int sx, sy, ex, ey; bool selection = region(sx, sy, ex, ey); - // fix scrolly so that <cx, cy> is always on screen if(cy < scrolly) scrolly = cy; - else - { + else { if(scrolly < 0) scrolly = 0; int h = 0; - for(int i = cy; i >= scrolly; i--) - { + for(int i = cy; i >= scrolly; i--) { int width, height; text_bounds(lines[i].text, width, height, maxwidth); if(h + height > pixelheight) { scrolly = i+1; break; } h += height; } } - - if(selection) - { + if(selection) { // convert from cursor coords into pixel coords int psx, psy, pex, pey; text_pos(lines[sy].text, sx, psx, psy, maxwidth); text_pos(lines[ey].text, ex, pex, pey, maxwidth); int maxy = lines.length(); int h = 0; - for(int i = scrolly; i < maxy; i++) - { + for(int i = scrolly; i < maxy; i++) { int width, height; text_bounds(lines[i].text, width, height, maxwidth); if(h + height > pixelheight) { maxy = i; break; } @@ -595,31 +470,26 @@ struct editor h += height; } maxy--; - - if(ey >= scrolly && sy <= maxy) - { + if(ey >= scrolly && sy <= maxy) { // crop top/bottom within window if(sy < scrolly) { sy = scrolly; psy = 0; psx = 0; } if(ey > maxy) { ey = maxy; pey = pixelheight - FONTH; pex = pixelwidth; } - hudnotextureshader->set(); gle::colorub(0xA0, 0x80, 0x80); gle::defvertex(2); gle::begin(GL_QUADS); - if(psy == pey) - { + if(psy == pey) { gle::attribf(x+psx, y+psy); gle::attribf(x+pex, y+psy); gle::attribf(x+pex, y+pey+FONTH); gle::attribf(x+psx, y+pey+FONTH); } - else - { gle::attribf(x+psx, y+psy); + else { + gle::attribf(x+psx, y+psy); gle::attribf(x+psx, y+psy+FONTH); gle::attribf(x+pixelwidth, y+psy+FONTH); gle::attribf(x+pixelwidth, y+psy); - if(pey-psy > FONTH) - { + if(pey-psy > FONTH) { gle::attribf(x, y+psy+FONTH); gle::attribf(x+pixelwidth, y+psy+FONTH); gle::attribf(x+pixelwidth, y+pey); @@ -634,17 +504,13 @@ struct editor hudshader->set(); } } - int h = 0; - for(int i = scrolly; i < lines.length(); i++) - { + for(int i = scrolly; i < lines.length(); i++) { int width, height; text_bounds(lines[i].text, width, height, maxwidth); if(h + height > pixelheight) break; - draw_text(lines[i].text, x, y+h, color>>16, (color>>8)&0xFF, color&0xFF, 0xFF, hit&&(cy==i)?cx:-1, maxwidth); - if(linewrap && height > FONTH) // line wrap indicator - { + if(linewrap && height > FONTH) { // line wrap indicator { hudnotextureshader->set(); gle::colorub(0x80, 0xA0, 0x80); gle::defvertex(2); @@ -666,24 +532,19 @@ static vector <editor*> editors; static editor *currentfocus() { return editors.length() ? editors.last() : NULL; } -static void readyeditors() -{ +static void readyeditors() { loopv(editors) editors[i]->active = (editors[i]->mode==EDITORFOREVER); } -static void flusheditors() -{ - loopvrev(editors) if(!editors[i]->active) - { +static void flusheditors() { + loopvrev(editors) if(!editors[i]->active) { editor *e = editors.remove(i); DELETEP(e); } } -static editor *useeditor(const char *name, int mode, bool focus, const char *initval = NULL) -{ - loopv(editors) if(strcmp(editors[i]->name, name) == 0) - { +static editor *useeditor(const char *name, int mode, bool focus, const char *initval = NULL) { + loopv(editors) if(strcmp(editors[i]->name, name) == 0) { editor *e = editors[i]; if(focus) { editors.add(e); editors.remove(i); } // re-position as last e->active = true; @@ -703,8 +564,7 @@ static editor *useeditor(const char *name, int mode, bool focus, const char *ini ICOMMAND(textlist, "", (), // @DEBUG return list of all the editors vector<char> s; - loopv(editors) - { + loopv(editors) { if(i > 0) s.put(", ", 2); s.put(editors[i]->name, strlen(editors[i]->name)); } @@ -731,19 +591,16 @@ TEXTCOMMAND(textsave, "s", (char *file), // saves the topmost (filename is opti top->save(); ); TEXTCOMMAND(textload, "s", (char *file), // loads into the topmost editor, returns filename if no args - if(*file) - { + if(*file) { top->setfile(path(file, true)); top->load(); } else if(top->filename) result(top->filename); ); -TEXTCOMMAND(textinit, "sss", (char *name, char *file, char *initval), // loads into named editor if no file assigned and editor has been rendered -{ +TEXTCOMMAND(textinit, "sss", (char *name, char *file, char *initval), { // loads into named editor if no file assigned and editor has been rendered { editor *e = NULL; loopv(editors) if(!strcmp(editors[i]->name, name)) { e = editors[i]; break; } - if(e && e->rendered && !e->filename && *file && (e->lines.empty() || (e->lines.length() == 1 && !strcmp(e->lines[0].text, initval)))) - { + if(e && e->rendered && !e->filename && *file && (e->lines.empty() || (e->lines.length() == 1 && !strcmp(e->lines[0].text, initval)))) { e->setfile(path(file, true)); e->load(); } |
