#include #include #include "config.h" #include "manager.h" #include "throw.h" using namespace std; Manager::Manager() :activeIdx(-1){} Manager::~Manager(){ for(Buffer *buffer : buffers){ delete buffer; } } void Manager::receive(const Command &cmd){ if(cmd[0]=="open_file"){ buffers.push_back(new Buffer(this)); buffers.back()->receive(cmd); activeIdx=buffers.size()-1; } else if(cmd[0]=="open_prompt"){ Maybe mfname=promptString("open: "); if(mfname.isNothing())return; buffers.push_back(new Buffer(this)); buffers.back()->receive({"open_file",mfname.fromJust()}); activeIdx=buffers.size()-1; } else if(cmd[0]=="save_prompt"){ if(activeIdx==-1){ receive({"error","No buffers open!"}); return; } Maybe mfname=promptString("save: "); if(mfname.isNothing())return; buffers[activeIdx]->filename=mfname.fromJust(); buffers[activeIdx]->receive({"save_file"}); } else if(cmd[0]=="close_tab"){ if(activeIdx==-1){ receive({"error","No buffers open!"}); return; } if(buffers[activeIdx]->isDirty()){ char choice=promptChar("Unsaved changes; discard? [y/N]","yn",'n'); if(choice=='n')return; } delete buffers[activeIdx]; buffers.erase(buffers.begin()+activeIdx); if(activeIdx>0)activeIdx--; if(buffers.size()==0)buffers.push_back(new Buffer(this)); } else if(cmd[0]=="cycle_tabs"){ if(activeIdx==-1){ receive({"error","No buffers open!"}); return; } activeIdx=(activeIdx+1)%buffers.size(); } else if(cmd[0]=="cycle_tabs_back"){ if(activeIdx==-1){ receive({"error","No buffers open!"}); return; } activeIdx=(activeIdx+buffers.size()-1)%buffers.size(); } else if(cmd[0]=="error"||cmd[0]=="info"){ if(!have_screen)return; bool isError=cmd[0]=="error"; pushcursor(); Size termsize=gettermsize(); Style style={isError?1:9,9,isError,false}; fillrect(0,termsize.h-1,termsize.w,1,' '); moveto(0,termsize.h-1); setstyle(&style); tprintf("%s",cmd[1].data()); popcursor(); } else if(cmd[0]=="quit_app"){ Size termsize=gettermsize(); for(i64 i=0;i<(i64)buffers.size();i++){ if(!buffers[i]->isDirty())continue; activeIdx=i; buffers[activeIdx]->show(0,1,termsize.w,termsize.h-2); show(); if(promptChar("Unsaved changes in this buffer; discard? [y/N]","yn",'n')=='n'){ return; } } should_quit=true; } else if(cmd[0]=="display_prompt"){ Maybe mline=promptString(":"); if(mline.isJust())receive({"info",{mline.fromJust()}}); } else { if(activeIdx==-1)bel(); else buffers[activeIdx]->receive(cmd); } } void Manager::show(){ if(buffers.size()==0)return; string bartext; i64 hilight_start=-1,hilight_len; for(size_t i=0;ifilename.size()==0?"":buffers[i]->filename; string tabtext=" "; for(size_t j=0;j=0;nameidx--){ if(fname[nameidx]=='/'){ nameidx++; break; } } //+1 because the first char was already appended tabtext+=fname.substr(nameidx+1,string::npos); if(buffers[i]->isDirty())tabtext+='*'; tabtext+=' '; if((i64)i==activeIdx){ hilight_start=bartext.size(); hilight_len=tabtext.length(); } bartext+=tabtext; } assert(hilight_start!=-1); Size termsize=gettermsize(); i64 print_start=-1; if(bartext.size()<=(size_t)termsize.w){ print_start=0; } else { print_start=hilight_start+hilight_len/2-termsize.w/2; if(print_start<0){ print_start=0; } else if(print_start+(size_t)termsize.w>=bartext.size()){ print_start=bartext.size()-termsize.w; } } Style backStyle={7,0,false,false}; Style activeStyle={7,4,true,false}; pushcursor(); moveto(0,0); setstyle(&backStyle); fillrect(0,0,termsize.w,1,' '); if(print_start>=hilight_start+hilight_len||print_start+termsize.w<=hilight_start){ string text=bartext.substr(print_start,termsize.w); tprintf("%s",text.data()); } else { string text=bartext.substr(print_start,hilight_start-print_start); tprintf("%s",text.data()); text=bartext.substr(hilight_start,hilight_len); setstyle(&activeStyle); tprintf("%s",text.data()); text=bartext.substr(hilight_start+hilight_len,termsize.w-(hilight_start+hilight_len-print_start)); setstyle(&backStyle); tprintf("%s",text.data()); } popcursor(); } class TermioRAII{ bool *variable; public: TermioRAII(bool *variable):variable(variable){ initscreen(); initkeyboard(true); *variable=true; } ~TermioRAII(){ *variable=false; endkeyboard(); endscreen(); } }; int Manager::io(){ TermioRAII termioRAII(&have_screen); if(activeIdx==-1){ buffers.push_back(new Buffer(this)); activeIdx=0; } while(true){ Size termsize=gettermsize(); show(); buffers[activeIdx]->show(0,1,termsize.w,termsize.h-2); redraw(); int key=tgetkey(); if(key<0){ cerr<<"Input read error: key="< ret=global_keyinput.input(key); if(ret.isLeft()){ if(!ret.fromLeft()){ bel(); receive({"error","Unbound key sequence"}); } } else { receive(ret.fromRight()); } if(should_quit)break; } return 0; } string truncatePromptString(const string &promptString,i64 width){ if((i64)promptString.size()>width*3/4){ string truncated(promptString.begin(),promptString.begin()+(width*3/4-3)); return truncated+"..."; } else { return promptString; } } //Returns entered text, or Nothing if cancelled Maybe Manager::promptString(const string &promptString){ Size termsize=gettermsize(); moveto(0,termsize.h-1); string truncated=truncatePromptString(promptString,termsize.w); i64 leftX=truncated.size(); Style promptStyle={9,9,false,false}; setstyle(&promptStyle); tprintf("%s",truncated.data()); global_keyinput.cancel(); string inputLine; Buffer promptBuffer(this,true); while(true){ termsize=gettermsize(); promptBuffer.show(leftX,termsize.h-1,termsize.w-leftX,1); redraw(); int key=tgetkey(); if(key<0){ cerr<<"Input read error in promptString: key="< ret=global_keyinput.input(key); if(ret.isLeft()){ if(!ret.fromLeft()){ bel(); } } else { Command cmd=ret.fromRight();; if(cmd[0]=="insert_newline"){ termsize=gettermsize(); fillrect(0,termsize.h-1,termsize.w,1,' '); return promptBuffer.getText(); } else if(cmd[0]=="cancel"){ termsize=gettermsize(); fillrect(0,termsize.h-1,termsize.w,1,' '); return {}; } else { promptBuffer.receive(cmd); } } } } char Manager::promptChar(const string &promptString,const string &options,char def){ Size termsize=gettermsize(); moveto(0,termsize.h-1); string truncated=truncatePromptString(promptString,termsize.w); Style promptStyle={9,9,false,false}; setstyle(&promptStyle); tprintf("%s",truncated.data()); redraw(); global_keyinput.cancel(); while(true){ int key=tgetkey(); if(key<0){ cerr<<"Input read error in promptChar: key="<=127){ bel(); continue; } key=tolower(key); if(options.find((char)key)!=string::npos){ termsize=gettermsize(); fillrect(0,termsize.h-1,termsize.w,1,' '); return (char)key; } bel(); } }