1 struct editline
2 {
3 enum { CHUNKSIZE = 256 };
4
5 char *text;
6 int len, maxlen;
7
editlineeditline8 editline() : text(NULL), len(0), maxlen(0) {}
editlineeditline9 editline(const char *init) : text(NULL), len(0), maxlen(0)
10 {
11 set(init);
12 }
13
emptyeditline14 bool empty() { return len <= 0; }
15
cleareditline16 void clear()
17 {
18 DELETEA(text);
19 len = maxlen = 0;
20 }
21
22 bool grow(int total, const char *fmt = "", ...)
23 {
24 if(total + 1 <= maxlen) return false;
25 maxlen = (total + CHUNKSIZE) - total%CHUNKSIZE;
26 char *newtext = new char[maxlen];
27 if(fmt)
28 {
29 va_list args;
30 va_start(args, fmt);
31 vformatstring(newtext, fmt, args, maxlen);
32 va_end(args);
33 }
34 else newtext[0] = '\0';
35 DELETEA(text);
36 text = newtext;
37 return true;
38 }
39
40 void set(const char *str, int slen = -1)
41 {
42 if(slen < 0)
43 {
44 slen = strlen(str);
45 if(!grow(slen, "%s", str)) memcpy(text, str, slen + 1);
46 }
47 else
48 {
49 grow(slen);
50 memcpy(text, str, slen);
51 text[slen] = '\0';
52 }
53 len = slen;
54 }
55
prependeditline56 void prepend(const char *str)
57 {
58 int slen = strlen(str);
59 if(!grow(slen + len, "%s%s", str, text ? text : ""))
60 {
61 memmove(&text[slen], text, len + 1);
62 memcpy(text, str, slen + 1);
63 }
64 len += slen;
65 }
66
appendeditline67 void append(const char *str)
68 {
69 int slen = strlen(str);
70 if(!grow(len + slen, "%s%s", text ? text : "", str)) memcpy(&text[len], str, slen + 1);
71 len += slen;
72 }
73
74 bool read(stream *f, int chop = -1)
75 {
76 if(chop < 0) chop = INT_MAX; else chop++;
77 set("");
78 while(len + 1 < chop && f->getline(&text[len], min(maxlen, chop) - len))
79 {
80 len += strlen(&text[len]);
81 if(len > 0 && text[len-1] == '\n')
82 {
83 text[--len] = '\0';
84 return true;
85 }
86 if(len + 1 >= maxlen && len + 1 < chop) grow(len + CHUNKSIZE, "%s", text);
87 }
88 if(len + 1 >= chop)
89 {
90 char buf[CHUNKSIZE];
91 while(f->getline(buf, sizeof(buf)))
92 {
93 int blen = strlen(buf);
94 if(blen > 0 && buf[blen-1] == '\n') return true;
95 }
96 }
97 return len > 0;
98 }
99
deleditline100 void del(int start, int count)
101 {
102 if(!text) return;
103 if(start < 0) { count += start; start = 0; }
104 if(count <= 0 || start >= len) return;
105 if(start + count > len) count = len - start - 1;
106 memmove(&text[start], &text[start+count], len + 1 - (start + count));
107 len -= count;
108 }
109
chopeditline110 void chop(int newlen)
111 {
112 if(!text) return;
113 len = clamp(newlen, 0, len);
114 text[len] = '\0';
115 }
116
117 void insert(char *str, int start, int count = 0)
118 {
119 if(count <= 0) count = strlen(str);
120 start = clamp(start, 0, len);
121 grow(len + count, "%s", text ? text : "");
122 memmove(&text[start + count], &text[start], len - start + 1);
123 memcpy(&text[start], str, count);
124 len += count;
125 }
126
combinelineseditline127 void combinelines(vector<editline> &src)
128 {
129 if(src.empty()) set("");
130 else loopv(src)
131 {
132 if(i) append("\n");
133 if(!i) set(src[i].text, src[i].len);
134 else insert(src[i].text, len, src[i].len);
135 }
136 }
137 };
138
139 struct editor
140 {
141 enum { SCROLLEND = INT_MAX };
142
143 int mode; //editor mode - 1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes)
144 bool active, rendered, unfocus;
145 const char *name;
146 const char *filename;
147
148 int cx, cy; // cursor position - ensured to be valid after a region() or currentline()
149 int mx, my; // selection mark, mx=-1 if following cursor - avoid direct access, instead use region()
150 int maxx, maxy; // maxy=-1 if unlimited lines, 1 if single line editor
151
152 int scrolly; // vertical scroll offset
153
154 bool linewrap;
155 int pixelwidth; // required for up/down/hit/draw/bounds
156 int pixelheight; // -1 for variable sized, i.e. from bounds()
157
158 vector<editline> lines; // MUST always contain at least one line!
159
editoreditor160 editor(const char *name, int mode, const char *initval) :
161 mode(mode), active(true), rendered(false), unfocus(false), name(newstring(name)), filename(NULL),
162 cx(0), cy(0), mx(-1), my(-1), maxx(-1), maxy(-1), scrolly(mode==EDITORREADONLY ? SCROLLEND : 0), linewrap(false), pixelwidth(-1), pixelheight(-1)
163 {
164 //printf("editor %08x '%s'\n", this, name);
165 clear(initval ? initval : "");
166 }
167
~editoreditor168 ~editor()
169 {
170 //printf("~editor %08x '%s'\n", this, name);
171 DELETEA(name);
172 DELETEA(filename);
173 clear(NULL);
174 }
175
emptyeditor176 bool empty() { return lines.length() == 1 && lines[0].empty(); }
177
178 void clear(const char *init = "")
179 {
180 cx = cy = 0;
181 mx = my = -1;
182 mark(false);
183 loopv(lines) lines[i].clear();
184 lines.shrink(0);
185 if(init)
186 {
187 lines.add().set(init);
188 cx = lines[0].len;
189 }
190 }
191
updateheighteditor192 void updateheight()
193 {
194 int width;
195 text_bounds(lines[0].text, width, pixelheight, pixelwidth);
196 }
197
setfileeditor198 void setfile(const char *fname)
199 {
200 DELETEA(filename);
201 if(fname) filename = newstring(fname);
202 }
203
loadeditor204 void load()
205 {
206 if(!filename) return;
207 clear(NULL);
208 stream *file = openutf8file(filename, "r");
209 if(file)
210 {
211 while(lines.add().read(file, maxx) && (maxy < 0 || lines.length() <= maxy));
212 lines.pop().clear();
213 delete file;
214 }
215 if(lines.empty()) lines.add().set("");
216 }
217
saveeditor218 void save()
219 {
220 if(!filename) return;
221 stream *file = openutf8file(filename, "w");
222 if(!file) return;
223 loopv(lines) file->putline(lines[i].text);
224 delete file;
225 }
226
markeditor227 void mark(bool enable)
228 {
229 mx = enable ? cx : -1;
230 my = cy;
231 }
232
selectalleditor233 void selectall()
234 {
235 mx = my = INT_MAX;
236 cx = cy = 0;
237 }
238
239 // constrain results to within buffer - s=start, e=end, return true if a selection range
240 // also ensures that cy is always within lines[] and cx is valid
regioneditor241 bool region(int &sx, int &sy, int &ex, int &ey)
242 {
243 int n = lines.length();
244 if(!n)
245 {
246 lines.add().set("");
247 n = 1;
248 }
249 if(cy < 0) cy = 0;
250 else if(cy >= n) cy = n-1;
251 int len = lines[cy].len;
252 if(cx < 0) cx = 0;
253 else if(cx > len) cx = len;
254 if(mx >= 0)
255 {
256 if(my < 0) my = 0;
257 else if(my >= n) my = n-1;
258 len = lines[my].len;
259 if(mx > len) mx = len;
260 }
261 sx = (mx >= 0) ? mx : cx;
262 sy = (mx >= 0) ? my : cy;
263 ex = cx;
264 ey = cy;
265 if(sy > ey) { swap(sy, ey); swap(sx, ex); }
266 else if(sy == ey && sx > ex) swap(sx, ex);
267 return (sx != ex) || (sy != ey);
268 }
269
regioneditor270 bool region() { int sx, sy, ex, ey; return region(sx, sy, ex, ey); }
271
272 // also ensures that cy is always within lines[] and cx is valid
currentlineeditor273 editline ¤tline()
274 {
275 int n = lines.length();
276 if(!n)
277 {
278 lines.add().set("");
279 n = 1;
280 }
281 if(cy < 0) cy = 0;
282 else if(cy >= n) cy = n-1;
283 if(cx < 0) cx = 0;
284 else if(cx > lines[cy].len) cx = lines[cy].len;
285 return lines[cy];
286 }
287
copyselectiontoeditor288 void copyselectionto(editor *b)
289 {
290 if(b == this) return;
291
292 b->clear(NULL);
293 int sx, sy, ex, ey;
294 region(sx, sy, ex, ey);
295 loopi(1+ey-sy)
296 {
297 if(b->maxy != -1 && b->lines.length() >= b->maxy) break;
298 int y = sy+i;
299 char *line = lines[y].text;
300 int len = lines[y].len;
301 if(y == sy && y == ey)
302 {
303 line += sx;
304 len = ex - sx;
305 }
306 else if(y == sy) line += sx;
307 else if(y == ey) len = ex;
308 b->lines.add().set(line, len);
309 }
310 if(b->lines.empty()) b->lines.add().set("");
311 }
312
tostringeditor313 char *tostring()
314 {
315 int len = 0;
316 loopv(lines) len += lines[i].len + 1;
317 char *str = newstring(len);
318 int offset = 0;
319 loopv(lines)
320 {
321 editline &l = lines[i];
322 memcpy(&str[offset], l.text, l.len);
323 offset += l.len;
324 str[offset++] = '\n';
325 }
326 str[offset] = '\0';
327 return str;
328 }
329
selectiontostringeditor330 char *selectiontostring()
331 {
332 vector<char> buf;
333 int sx, sy, ex, ey;
334 region(sx, sy, ex, ey);
335 loopi(1+ey-sy)
336 {
337 int y = sy+i;
338 char *line = lines[y].text;
339 int len = lines[y].len;
340 if(y == sy && y == ey)
341 {
342 line += sx;
343 len = ex - sx;
344 }
345 else if(y == sy) line += sx;
346 else if(y == ey) len = ex;
347 buf.put(line, len);
348 buf.add('\n');
349 }
350 buf.add('\0');
351 return newstring(buf.getbuf(), buf.length()-1);
352 }
353
removelineseditor354 void removelines(int start, int count)
355 {
356 loopi(count) lines[start+i].clear();
357 lines.remove(start, count);
358 }
359
deleditor360 bool del() // removes the current selection (if any)
361 {
362 int sx, sy, ex, ey;
363 if(!region(sx, sy, ex, ey))
364 {
365 mark(false);
366 return false;
367 }
368 if(sy == ey)
369 {
370 if(sx == 0 && ex == lines[ey].len) removelines(sy, 1);
371 else lines[sy].del(sx, ex - sx);
372 }
373 else
374 {
375 if(ey > sy+1) { removelines(sy+1, ey-(sy+1)); ey = sy+1; }
376 if(ex == lines[ey].len) removelines(ey, 1); else lines[ey].del(0, ex);
377 if(sx == 0) removelines(sy, 1); else lines[sy].del(sx, lines[sy].len - sx);
378 }
379 if(lines.empty()) lines.add().set("");
380 mark(false);
381 cx = sx;
382 cy = sy;
383 editline ¤t = currentline();
384 if(cx >= current.len && cy < lines.length() - 1)
385 {
386 current.append(lines[cy+1].text);
387 removelines(cy + 1, 1);
388 }
389 return true;
390 }
391
inserteditor392 void insert(char ch)
393 {
394 del();
395 editline ¤t = currentline();
396 if(ch == '\n')
397 {
398 if(maxy == -1 || cy < maxy-1)
399 {
400 editline newline(¤t.text[cx]);
401 current.chop(cx);
402 cy = min(lines.length(), cy+1);
403 lines.insert(cy, newline);
404 }
405 else current.chop(cx);
406 cx = 0;
407 }
408 else if(ch != '\r')
409 {
410 int len = current.len;
411 if(maxx < 0 || len <= maxx-1) current.insert(&ch, cx++, 1);
412 }
413 }
414
inserteditor415 void insert(const char *s)
416 {
417 while(*s) insert(*s++);
418 }
419
insertallfromeditor420 void insertallfrom(editor *b)
421 {
422 if(b == this) return;
423
424 del();
425
426 if(b->lines.length() == 1 || maxy == 1)
427 {
428 editline ¤t = currentline();
429 char *str = b->lines[0].text;
430 int slen = b->lines[0].len;
431 if(maxx >= 0 && b->lines[0].len + cx > maxx) slen = maxx-cx;
432 if(slen > 0)
433 {
434 int len = current.len;
435 if(maxx >= 0 && slen + cx + len > maxx) len = max(0, maxx-(cx+slen));
436 current.insert(str, cx, slen);
437 cx += slen;
438 }
439 }
440 else
441 {
442 loopv(b->lines)
443 {
444 if(!i)
445 {
446 lines[cy++].append(b->lines[i].text);
447 }
448 else if(i >= b->lines.length())
449 {
450 cx = b->lines[i].len;
451 lines[cy].prepend(b->lines[i].text);
452 }
453 else if(maxy < 0 || lines.length() < maxy) lines.insert(cy++, editline(b->lines[i].text));
454 }
455 }
456 }
457
scrollupeditor458 void scrollup()
459 {
460 mark(false);
461 cy--;
462 }
463
scrolldowneditor464 void scrolldown()
465 {
466 mark(false);
467 cy++;
468 }
keyeditor469 void key(int code)
470 {
471 switch(code)
472 {
473 case SDLK_UP:
474 mark(false);
475 if(linewrap)
476 {
477 int x, y;
478 char *str = currentline().text;
479 text_pos(str, cx+1, x, y, pixelwidth, TEXT_NO_INDENT);
480 if(y > 0) { cx = text_visible(str, x, y-FONTH, pixelwidth, TEXT_NO_INDENT); break; }
481 }
482 cy--;
483 break;
484 case SDLK_DOWN:
485 mark(false);
486 if(linewrap)
487 {
488 int x, y, width, height;
489 char *str = currentline().text;
490 text_pos(str, cx, x, y, pixelwidth, TEXT_NO_INDENT);
491 text_bounds(str, width, height, 0, 0, pixelwidth, TEXT_NO_INDENT);
492 y += FONTH;
493 if(y < height) { cx = text_visible(str, x, y, pixelwidth, TEXT_NO_INDENT); break; }
494 }
495 cy++;
496 break;
497 case SDLK_PAGEUP:
498 mark(false);
499 cy-=pixelheight/FONTH;
500 break;
501 case SDLK_PAGEDOWN:
502 mark(false);
503 cy+=pixelheight/FONTH;
504 break;
505 case SDLK_HOME:
506 mark(false);
507 cx = 0;
508 break;
509 case SDLK_END:
510 mark(false);
511 cx = INT_MAX;
512 break;
513 case SDLK_LEFT:
514 mark(false);
515 cx--;
516 break;
517 case SDLK_RIGHT:
518 mark(false);
519 cx++;
520 break;
521 case SDLK_DELETE:
522 if(!del())
523 {
524 editline ¤t = currentline();
525 if(cx < current.len) current.del(cx, 1);
526 else if(cy < lines.length()-1)
527 { //combine with next line
528 current.append(lines[cy+1].text);
529 removelines(cy+1, 1);
530 }
531 }
532 break;
533 case SDLK_BACKSPACE:
534 if(!del())
535 {
536 editline ¤t = currentline();
537 if(cx > 0) current.del(--cx, 1);
538 else if(cy > 0)
539 { //combine with previous line
540 cx = lines[cy-1].len;
541 lines[cy-1].append(current.text);
542 removelines(cy--, 1);
543 }
544 }
545 break;
546 case SDLK_LSHIFT:
547 case SDLK_RSHIFT:
548 break;
549 case SDLK_v:
550 if(SDL_GetModState()&MOD_KEYS)
551 {
552 char *pastebuf = pastetext();
553 if(pastebuf)
554 {
555 insert(pastebuf);
556 delete[] pastebuf;
557 }
558 }
559 break;
560 case SDLK_RETURN:
561 insert('\n');
562 break;
563 }
564 }
565
inputeditor566 void input(const char *str, int len)
567 {
568 loopi(len) insert(str[i]);
569 }
570
hiteditor571 void hit(int hitx, int hity, bool dragged)
572 {
573 int maxwidth = linewrap ? pixelwidth : -1, h = 0;
574 for(int i = scrolly; i < lines.length(); i++)
575 {
576 int width, height;
577 text_bounds(lines[i].text, width, height, 0, 0, maxwidth, TEXT_NO_INDENT);
578 if(h + height > pixelheight) break;
579
580 if(hity >= h && hity <= h+height)
581 {
582 int x = text_visible(lines[i].text, hitx, hity-h, maxwidth, TEXT_NO_INDENT);
583 if(dragged) { mx = x; my = i; } else { cx = x; cy = i; };
584 break;
585 }
586 h += height;
587 }
588 }
589
limitscrollyeditor590 int limitscrolly()
591 {
592 int maxwidth = linewrap ? pixelwidth : -1, slines = lines.length();
593 for(int ph = pixelheight; slines > 0 && ph > 0;)
594 {
595 int width, height;
596 text_bounds(lines[slines-1].text, width, height, 0, 0, maxwidth, TEXT_NO_INDENT);
597 if(height > ph) break;
598 ph -= height;
599 slines--;
600 }
601 return slines;
602 }
603
draweditor604 void draw(int x, int y, int color, int alpha, bool hit)
605 {
606 int h = 0, maxwidth = linewrap ? pixelwidth : -1,
607 starty = scrolly, sx = 0, sy = 0, ex = 0, ey = 0;
608 bool selection = region(sx, sy, ex, ey);
609 if(starty == SCROLLEND) // fix scrolly so that <cx, cy> is always on screen
610 {
611 cy = lines.length()-1;
612 starty = 0;
613 }
614 if(cy < starty) starty = cy;
615 else
616 {
617 int ch = 0;
618 for(int i = cy; i >= starty; i--)
619 {
620 int width, height;
621 text_bounds(lines[i].text, width, height, 0, 0, maxwidth, TEXT_NO_INDENT);
622 ch += height;
623 if(ch > pixelheight) { starty = i+1; break; }
624 }
625 }
626
627 if(selection)
628 {
629 int psx, psy, pex, pey; // convert from cursor coords into pixel coords
630 text_pos(lines[sy].text, sx, psx, psy, maxwidth, TEXT_NO_INDENT);
631 text_pos(lines[ey].text, ex, pex, pey, maxwidth, TEXT_NO_INDENT);
632 int maxy = lines.length();
633 int h = 0;
634 for(int i = starty; i < maxy; i++)
635 {
636 int width, height;
637 text_bounds(lines[i].text, width, height, 0, 0, maxwidth, TEXT_NO_INDENT);
638 if(h+height > pixelheight) { maxy = i; break; }
639 if(i == sy) psy += h;
640 if(i == ey) { pey += h; break; }
641 h += height;
642 }
643 maxy--;
644
645 if(ey >= starty && sy <= maxy)
646 {
647 if(sy < starty) // crop top/bottom within window
648 {
649 sy = starty;
650 psy = 0;
651 psx = 0;
652 }
653 if(ey > maxy)
654 {
655 ey = maxy;
656 pey = pixelheight-FONTH;
657 pex = pixelwidth;
658 }
659 hudnotextureshader->set();
660 gle::colorf(0.25f, 0.25f, 0.75f, alpha/255.f);
661 gle::defvertex(2);
662 gle::begin(GL_QUADS);
663 if(psy == pey)
664 {
665 gle::attribf(x+psx, y+psy);
666 gle::attribf(x+pex, y+psy);
667 gle::attribf(x+pex, y+pey+FONTH);
668 gle::attribf(x+psx, y+pey+FONTH);
669 }
670 else
671 { gle::attribf(x+psx, y+psy);
672 gle::attribf(x+psx, y+psy+FONTH);
673 gle::attribf(x+pixelwidth, y+psy+FONTH);
674 gle::attribf(x+pixelwidth, y+psy);
675 if(pey-psy > FONTH)
676 {
677 gle::attribf(x, y+psy+FONTH);
678 gle::attribf(x+pixelwidth, y+psy+FONTH);
679 gle::attribf(x+pixelwidth, y+pey);
680 gle::attribf(x, y+pey);
681 }
682 gle::attribf(x, y+pey);
683 gle::attribf(x, y+pey+FONTH);
684 gle::attribf(x+pex, y+pey+FONTH);
685 gle::attribf(x+pex, y+pey);
686 }
687 gle::end();
688 hudshader->set(); //?
689 }
690 }
691
692 for(int i = starty; i < lines.length(); i++)
693 {
694 int width, height;
695 text_bounds(lines[i].text, width, height, 0, 0, maxwidth, TEXT_NO_INDENT);
696 if(h+height > pixelheight) break;
697 draw_text(lines[i].text, x, y+h, color>>16, (color>>8)&0xFF, color&0xFF, alpha, TEXT_NO_INDENT, hit && (cy == i) ? cx : -1, maxwidth);
698 if(linewrap && height > FONTH) // line wrap indicator
699 {
700 hudnotextureshader->set();
701 gle::colorf(1, 1, 1, alpha/255.f);
702 gle::defvertex(2);
703 gle::begin(GL_TRIANGLE_STRIP);
704 gle::attribf(x, y+h+FONTH);
705 gle::attribf(x, y+h+height);
706 gle::attribf(x-FONTW/4, y+h+FONTH);
707 gle::attribf(x-FONTW/4, y+h+height);
708 gle::end();
709 hudshader->set();
710 }
711 h += height;
712 }
713 }
714 };
715
716 // a 'stack' where the last is the current focused editor
717 static vector <editor*> editors;
718 static editor *textfocus = NULL;
719
readyeditors()720 static void readyeditors()
721 {
722 loopv(editors) editors[i]->active = (editors[i]->mode>=EDITORFOREVER);
723 }
724
flusheditors()725 static void flusheditors()
726 {
727 loopvrev(editors) if(!editors[i]->active)
728 {
729 editor *e = editors.remove(i);
730 if(e == textfocus) textfocus = NULL;
731 delete e;
732 }
733 }
734
735 static editor *useeditor(const char *name, int mode, bool focus, const char *initval = NULL)
736 {
737 loopv(editors) if(!strcmp(editors[i]->name, name))
738 {
739 editor *e = editors[i];
740 if(focus) textfocus = e;
741 e->active = true;
742 return e;
743 }
744 if(mode < 0) return NULL;
745 editor *e = new editor(name, mode, initval);
746 editors.add(e);
747 if(focus) textfocus = e;
748 return e;
749 }
750
751 #if 0
752 static editor *findeditor(const char *name)
753 {
754 loopv(editors) if(strcmp(editors[i]->name, name) == 0) return editors[i];
755 return NULL;
756 }
757 #endif // 0
758
759 #define TEXTCOMMAND(f, s, d, body) ICOMMAND(0, f, s, d,\
760 if(!textfocus || identflags&IDF_WORLD) return;\
761 body\
762 )
763
764 ICOMMAND(0, textlist, "", (), // @DEBUG return list of all the editors
765 vector<char> s;
766 loopv(editors)
767 {
768 if(i > 0) s.put(", ", 2);
769 s.put(editors[i]->name, strlen(editors[i]->name));
770 }
771 s.add('\0');
772 result(s.getbuf());
773 );
774 TEXTCOMMAND(textshow, "", (), // @DEBUG return the start of the buffer
775 editline line;
776 line.combinelines(textfocus->lines);
777 result(line.text);
778 line.clear();
779 );
780 ICOMMAND(0, textfocus, "sis", (char *name, int *mode, char *initval), // focus on a (or create a persistent) specific editor, else returns current name
781 if(identflags&IDF_WORLD) return;
782 if(*name) useeditor(name, *mode<=0 ? EDITORFOREVER : *mode, true, initval);
783 else if(editors.length() > 0) result(editors.last()->name);
784 );
785 TEXTCOMMAND(textprev, "", (), editors.insert(0, textfocus); editors.pop();); // return to the previous editor
786 TEXTCOMMAND(textmode, "i", (int *m), // (1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes)) topmost editor, return current setting if no args
787 if(*m) textfocus->mode = *m;
788 else intret(textfocus->mode);
789 );
790 TEXTCOMMAND(textsave, "s", (char *file), // saves the topmost (filename is optional)
791 if(identflags&IDF_WORLD) return;
792 if(*file) textfocus->setfile(copypath(file));
793 textfocus->save();
794 );
795 TEXTCOMMAND(textload, "s", (char *file), // loads into the textfocusmost editor, returns filename if no args
796 if(identflags&IDF_WORLD) return;
797 if(*file)
798 {
799 textfocus->setfile(copypath(file));
800 textfocus->load();
801 }
802 else if(textfocus->filename) result(textfocus->filename);
803 );
804 ICOMMAND(0, textinit, "sss", (char *name, char *file, char *initval), // loads into named editor if no file assigned and editor has been rendered
805 {
806 if(identflags&IDF_WORLD) return;
807 editor *e = NULL;
808 loopv(editors) if(!strcmp(editors[i]->name, name)) { e = editors[i]; break; }
809 if(e && e->rendered && !e->filename && *file && (e->lines.empty() || (e->lines.length() == 1 && !strcmp(e->lines[0].text, initval))))
810 {
811 e->setfile(copypath(file));
812 e->load();
813 }
814 });
815
816 #define PASTEBUFFER "#pastebuffer"
817
818 TEXTCOMMAND(textcopy, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); textfocus->copyselectionto(b););
819 TEXTCOMMAND(textpaste, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); textfocus->insertallfrom(b););
820 TEXTCOMMAND(textmark, "i", (int *m), // (1=mark, 2=unmark), return current mark setting if no args
821 if(*m) textfocus->mark(*m==1);
822 else intret(textfocus->region() ? 1 : 2);
823 );
824 TEXTCOMMAND(textselectall, "", (), textfocus->selectall(););
825 TEXTCOMMAND(textclear, "", (), textfocus->clear(););
826 TEXTCOMMAND(textcurrentline, "", (), result(textfocus->currentline().text););
827
828 TEXTCOMMAND(textexec, "i", (int *selected), // execute script commands from the buffer (0=all, 1=selected region only)
829 char *script = *selected ? textfocus->selectiontostring() : textfocus->tostring();
830 execute(script);
831 delete[] script;
832 );
833
834 TEXTCOMMAND(textadd, "ss", (char *name, char *str), // loads into named editor if no file assigned and editor has been rendered
835 {
836 editor *e = NULL;
837 loopv(editors) if(!strcmp(editors[i]->name, name)) { e = editors[i]; break; }
838 if(e && e->rendered) e->insert(str);
839 });
840