// edit.cpp // Revision 7-feb-2005 //#include "cursor.h" #include "graphics.h" #include "error.h" #include "trace.h" #include "util.h" #include "sysvar.h" #include "edit.h" using util::to_string; #include #include #include #define ASSERT assert namespace sysvar= blassic::sysvar; using namespace blassic::file; namespace { std::deque history; size_t getmaxhistory () { return sysvar::get16 (sysvar::MaxHistory); } void showstring (BlFile & bf, const std::string & str) { for (size_t i= 0, l= str.size (); i < l; ++i) { unsigned char c= str [i]; if (c < 32) bf << '\\' << char (c + 'A'); else bf << static_cast (c); } bf.flush (); } class Edit { public: Edit (BlFile & bf, std::string & str, size_t npos, size_t inicol, bool lineend); ~Edit (); bool do_it (); private: BlFile & bf; std::string & str; size_t npos; size_t inicol; size_t width; bool textwindow; size_t histsize; size_t histpos; bool intagmode; bool lineend; std::string savestr; //void getwidth (); void back (); void forward (); void deletechar (); void showrest (); void showinitial (); void up (); void down (); }; Edit::Edit (BlFile & bf, std::string & str, size_t npos, size_t inicol, bool lineend) : bf (bf), str (str), npos (npos), inicol (inicol), width (bf.getwidth () ), textwindow (bf.istextwindow () ), histsize (history.size () ), histpos (histsize), intagmode (bf.istagactive () ), lineend (lineend) { TRACEFUNC (tr, "Edit::~Edit"); TRMESSAGE (tr, "lineend: " + to_string (lineend) ); if (textwindow) graphics::synchronize_suspend (); if (intagmode) bf.tagoff (); } Edit::~Edit () { TRACEFUNC (tr, "Edit::~Edit"); if (textwindow) graphics::synchronize_restart (); if (intagmode) bf.tag (); } void Edit::back () { if (textwindow) { --npos; bf.movecharback (); return; } if ( (npos-- + inicol) % width != 0) bf.movecharback (); else { bf.movecharup (); bf.movecharforward (width - 1); } } void Edit::forward () { ++npos; if (textwindow) { bf.movecharforward (); return; } if ( (npos + inicol) % width != 0) bf.movecharforward (); else { if (npos == str.size () ) { //bf.movecharforward (); //bf << '\n'; bf.endline (); bf.flush (); } else { //bf << '\r'; //bf.flush (); bf.movecharback (width - 1); bf.movechardown (); } } } void Edit::deletechar () { if (npos < str.size () ) { str.erase (npos, 1); // Vil chapuza. str+= ' '; showrest (); str.erase (str.size () - 1); } } void Edit::showrest () { //TRACEFUNC (tr, "Edit::showrest"); if (npos == str.size () ) return; //bf << str.substr (npos); //bf.flush (); showstring (bf, str.substr (npos) ); size_t l= str.size (); size_t nlines= (l + inicol) / width; size_t actualline= (npos + inicol) / width; size_t actualcol= (npos + inicol) % width; size_t lastcol= (l + inicol) % width; if (lastcol == 0 && nlines > 0) { if (! textwindow) { #ifndef __WIN32__ --nlines; // The cursor position remains // in the last valid column. lastcol= width - 1; //lastcol= width; #endif } } bf.movecharup (nlines - actualline); if (actualcol < lastcol) { //TRMESSAGE (tr, std::string ("moving back " + // to_string (lastcol - actualcol) ) ); bf.movecharback (lastcol - actualcol); } else if (actualcol > lastcol) { //TRMESSAGE (tr, "moving forward"); bf.movecharforward (actualcol - lastcol); } else if (actualcol == width - 1) { // Without this the cursor sometimes keep in the next line. bf.movecharback (); bf.movecharforward (); } } void Edit::showinitial () { //TRACEFUNC (tr, "Edit::showinitial"); //bf << '\r' << str; //bf.flush (); //bf << '\r'; //bf.movecharforward (inicol); showstring (bf, str); size_t l= str.size () + inicol; size_t nlines= l / width; size_t actualline= (npos + inicol) / width; size_t actualcol= (npos + inicol) % width; size_t lastcol= l % width; #ifndef __WIN32__ if (lastcol == 0 && nlines > 0) { --nlines; lastcol= width - 1; } #endif bf.movecharup (nlines - actualline); if (actualcol < lastcol) bf.movecharback (lastcol - actualcol); else bf.movecharforward (actualcol - lastcol); //bf << '\r'; //bf.flush (); //movecharforward (npos); } void Edit::up () { if (histpos > 0) { if (histpos == histsize) savestr= str; std::string::size_type oldsize= str.size (); --histpos; str= history [histpos]; while (npos > 0) back (); std::string::size_type actualsize= str.size (); if (oldsize > actualsize) { showstring (bf, std::string (oldsize, ' ') ); npos= oldsize; for (size_t i= 0; i < oldsize; ++i) back (); } //showinitial (); showstring (bf, str); npos= actualsize; } } void Edit::down () { if (histpos < histsize) { std::string::size_type oldsize= str.size (); ++histpos; if (histpos < histsize) str= history [histpos]; else str= savestr; while (npos > 0) back (); std::string::size_type actualsize= str.size (); if (oldsize > actualsize) { showstring (bf, std::string (oldsize, ' ') ); npos= oldsize; for (size_t i= 0; i < oldsize; ++i) back (); } //showinitial (); showstring (bf, str); npos= actualsize; } } bool Edit::do_it () { TRACEFUNC (tr, "Edit::do_it"); showinitial (); bool editing= true; bool retval= true; while (editing) { bf.showcursor (); //TRMESSAGE (tr, "Waiting key"); std::string key= bf.getkey (); //TRMESSAGE (tr, "Received key"); bf.hidecursor (); if (key.size () == 1) { unsigned char c= key [0]; switch (c) { case '\r': case '\n': editing= false; break; case '\x1B': editing= false; retval= false; break; case '\t': { //size_t n= npos + 1; //n+= 7 - (n % 8); //n-= npos ; size_t n= 8 - npos % 8; str.insert (npos, n, ' '); showrest (); for (size_t i= 0; i < n; ++i) forward (); } break; case '\3': // Ctrl-C editing= false; retval= false; break; case '\5': // Ctrl-E, test debug //bf << str.substr (npos) << '\n'; //bf.flush (); showstring (bf, str.substr (npos) ); //bf << '\n'; bf.endline (); bf.flush (); showinitial (); break; case '\x8': case '\x7F': if (npos > 0) { back (); deletechar (); } break; default: if (c >= ' ') { str.insert (npos, 1, c); showrest (); forward (); } } } else if (key == "RIGHT") { if (npos < str.size () ) { forward (); } } else if (key == "LEFT") { if (npos > 0) { back (); } } else if (key == "DELETE") { if (npos < str.size () ) deletechar (); } else if (key == "HOME") { while (npos > 0) back (); } else if (key == "END") { const size_t l= str.size (); while (npos < l) forward (); } else if (key == "UP") up (); else if (key == "DOWN") down (); } //hidecursor (); if (lineend) { // After exit, cursor must be positioned after the line edited. //bf << str.substr (npos) << '\n'; bf << str.substr (npos); bf.endline (); bf.flush (); } if (retval) { size_t maxhist= getmaxhistory (); if (maxhist == 0) maxhist= 1; if (histsize >= maxhist) { history.erase (history.begin (), history.begin () + histsize - maxhist + 1); } if (! str.empty () && (history.empty () || history [history.size () - 1] != str) ) history.push_back (str); } return retval; } } // namespace bool blassic::edit::editline (BlFile & bf, std::string & str, size_t npos, size_t inicol, bool lineend) { TRACEFUNC (tr, "editline - string"); { std::ostringstream oss; oss << "Inicol: " << inicol; TRMESSAGE (tr, oss.str () ); } Edit edit (bf, str, npos, inicol, lineend); return edit.do_it (); } #if 0 bool editline (BlFile & bf, Program & program, BlLineNumber bln, std::string & str) { TRACEFUNC (tr, "editline - line number->string"); std::string buffer; { BlFileOutString bfos; program.list (bln, bln, bfos); buffer= bfos.str (); if (buffer.empty () ) { //bfos << bln << " \n"; bfos << bln << ' '; bfos.endline (); buffer= bfos.str (); } } buffer.erase (buffer.size () - 1); TRMESSAGE (tr, std::string (1, '\'') + buffer + '\''); static const std::string number ("01234567890"); size_t inipos= buffer.find_first_of (number); ASSERT (inipos != std::string::npos); inipos= buffer.find_first_not_of (number, inipos); ASSERT (inipos != std::string::npos); ++inipos; bool r; if ( (r= editline (bf, buffer, inipos) ) == true) str= buffer; return r; } bool editline (BlFile & bf, Program & program, BlLineNumber bln) { TRACEFUNC (tr, "editline - line number"); std::string buffer; bool r; if ( (r= editline (bf, program, bln, buffer) ) == true) { CodeLine code; code.scan (buffer); BlLineNumber nline= code.number (); if (nline == LineDirectCommand) throw ErrBlassicInternal; else { if (code.empty () ) program.deletelines (nline, nline); else program.insert (code); } } return r; } #endif // End of edit.cpp