/* TUIpeer works in conjunction with an implementation of java.awt.Toolkit to provide a Text User Interface for programs using the Java AWT. Copyright (C) 1997-2000 Stuart D. Gathman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #pragma implementation "textedit.h" #include #include #include "textedit.h" #include /* The ImageWin class from edximg could be used (modulo load/store), * and can display attributes, but it imposes a maximum line length. * Very long lines can be an efficiency problem with this model * because tabs are expanded for display. */ TextEdit::TextEdit(StringList *model): TextBrowse(view), view(model ? *model : *new StringList), txt(view.txt) { txt[0] = '~'; // default string wrapmargin = 0; } TextEdit::~TextEdit() { delete &txt; } string TextEdit::pasteBuffer; void TextEdit::key(int ky) { if (!readonly) switch (ky) { case KEY_TAB: if (insert_mode) { insch('\t'); return; } break; case KEY_ENTER: if (insert_mode) { int r = getRow(); string &line = txt[r]; insertln(r + 1); int idx = virtpos(line.c_str()).mappos(getCol()); txt[++r] = string(line,idx); // make enter a hard return by removing trailing space while (idx && isspace(line[idx - 1])) --idx; line.erase(idx); move(r,0); int row = getRow() - topline; repaint(Rect(row - 1,0,row,width)); reform(); return; } break; case KEY_BACKSPACE: if (getCol() <= 0) { int r = getRow(); if (r > 0) { move(r,-1); txt[r-1] += txt[r]; repaintToEOL(); deleteln(r); reform(); } return; } break; } if (commandMode()) switch (ky) { case 'C': // change to eol if (isGap()) { int crow,ccol; getyx(crow,ccol); move(crow + 1,-1); mark(); move(crow,ccol); } commandMode(false); return; case 'm': // mark mark(); return; case 'y': // yank pasteBuffer = getSelectedText(); return; case 'd': //cut pasteBuffer = getSelectedText(); if (!readonly) replaceText(""); return; case 'p': // pull if (!readonly) replaceText(pasteBuffer); return; } EditorBase::key(ky); } void TextEdit::addch(char c) { if (!isGap()) { replaceText(string(1,c)); return; } int r = getRow(); if (r >= txt.size()) txt.add(c); else { string &line = txt[r]; virtpos p(line.c_str()); if (p.mappos(getCol()) < line.length()) line[p.idx] = c; else { insch(c); return; } } repaintToEOL(); move(r,getCol() + 1); modified = true; } void TextEdit::insch(char c) { if (!isGap()) { replaceText(string(1,c)); return; } int r = getRow(); if (r >= txt.size()) txt.add(c); else { string &line = txt[r]; virtpos p(line.c_str()); if (p.mappos(getCol()) >= line.length()) { int pos = p.pos; while (pos < getCol()) { line += ' '; virtpos p1(line.c_str()); p1.mappos(getCol()); pos = p1.pos; } line += c; } else line.replace(p.idx,0,c); } repaintToEOL(); move(r,getCol() + 1); reform(true); modified = true; } void TextEdit::repaintToEOL(int r,int col) { int row = r - topline; repaint(Rect(row,col-leftcol,row,width)); } void TextEdit::repaintToEOL() { repaintToEOL(getRow(),getCol()); } void TextEdit::delch() { if (!isGap()) { replaceText(""); return; } string &line = txt[getRow()]; int idx = virtpos(line.c_str()).mappos(getCol()); if (idx < line.length()) { line.erase(idx,1); repaintToEOL(); reform(); modified = true; } } void TextEdit::insertln(int r) { txt.insertln(r); slide(Rect(r - topline,0,height,width),DIR_DOWN,1); modified = true; } void TextEdit::deleteln(int r) { txt.deleteln(r); slide(Rect(r - topline + 1,0,height,width),DIR_UP,1); modified = true; } void TextEdit::clrtoeol() { string &line = txt[getRow()]; line.erase(virtpos(line.c_str()).mappos(getCol())); repaintToEOL(); modified = true; } static int wordlen(const char *s) { int i; for (i = 0; s[i] && !isspace(s[i]); ++i) ; return i; } static int wraplen(const string &str,int margin) { int i = str.length(); const char *s = str.c_str(); if (i > margin) i = margin; while (i > 0 && isspace(s[i-1])) --i; while (i > 0 && !isspace(s[i-1])) --i; return i; } void TextEdit::reform(bool hardret) { int r1 = getRow(); int c1 = getCol(); int r; for (r = r1; r < txt.size(); hardret = false) { string &line = txt[r++]; if (wrapmargin <= wordlen(line.c_str())) break; virtpos p(line.c_str()); int len = line.length(); if (r >= txt.size() || !len || !isspace(line[len - 1])) hardret = true; int mlen = p.mappos(wrapmargin); if (len > mlen) { int w = wraplen(line,mlen); if (hardret) insertln(r); string &cont = txt[r]; cont = string(line,w) + cont; int c = p.mappos(getCol()); line.erase(w); if (c >= w && r - 1 == getRow()) { c1 = w; move(r,c - w); } continue; } string &nxtl = txt[r]; if (hardret || p.mapidx(len) + wordlen(nxtl.c_str()) >= wrapmargin) break; if (nxtl.length() == 0) break; line += nxtl; deleteln(r--); } repaintToEOL(r1,c1); if (++r1 < r) repaint(Rect(r1 - topline,0,r - topline - 1,width)); } void TextEdit::cmd(int c) { if (!readonly) switch (c) { case EDIT_REFORM: reform(); setfocus(this); return; } EditorBase::cmd(c); } void TextEdit::replaceText(const string &str,int start,int end) { TextSelection::select(start,end); replaceText(str); } void TextEdit::replaceText(const string &s) { int r1 = getStartRow(); virtpos p1(txt[r1].c_str()); int c1pos = getStartCol(); int c1 = p1.mappos(c1pos); int r2 = getEndRow(); int c2 = getEndCol(); if (r1 == r2) c2 = p1.mappos(c2); else c2 = virtpos(txt[r2].c_str()).mappos(c2); int len = s.length(); if (!isGap()) mark(); else if (len == 0) return; // replacing "" with "" // r1,r2 are 1st and last affect lines // delete enclosed lines while (r1 < r2 - 1) deleteln(--r2); int r = r1; int pos = 0; do { int n = s.find('\n',pos); if (n < 0) { len -= pos; if (r == r1) { if (r1 == r2) { if (r1 >= txt.size()) txt.add(string(s,pos)); else { txt[r1].replace(c1,c2-c1,string(s,pos)); } } else { string &line(txt[r1]); if (line.length()) line.replace(c1,s.npos,string(s,pos) + txt[r2]); else line = string(s,pos) + txt[r2]; deleteln(r2--); } c2 = c1 + len; } else { txt[r2].replace(0,c2,string(s,pos)); c2 = len; } break; } if (r++ == r1) { if (r1 >= txt.size()) insertln(r1); if (r1 == r2) { insertln(++r2); txt[r2] = string(txt[r1],c2); c2 = 0; } string &line(txt[r1]); if (line.length()) line.replace(c1,s.npos,string(s,pos,n-pos)); else line = string(s,pos,n-pos); } else { insertln(r2); txt[r2++] = string(s,pos,n - pos); } pos = n; } while (++pos < len); repaintToEOL(r1,c1pos); repaint(Rect(++r1 - topline,0,r2 - topline,width)); move(r2,virtpos(txt[r2].c_str()).mapidx(c2)); modified = true; }