From a8e19d5d8057e82cbda2705d755f3d4e1d3da20a Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Sun, 1 May 2016 05:25:14 -0400 Subject: remove references to files outside of this repo (commit the files into this repo) --- src/dynmenu.cpp | 821 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 821 insertions(+) create mode 100644 src/dynmenu.cpp (limited to 'src/dynmenu.cpp') diff --git a/src/dynmenu.cpp b/src/dynmenu.cpp new file mode 100644 index 0000000..d231639 --- /dev/null +++ b/src/dynmenu.cpp @@ -0,0 +1,821 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class DynmenuView; +typedef DynmenuView *DynmenuViewRef; // boost::shared_ptr + +class Dynmenu +{ + std::string m_title; + std::string m_welcomeText; + std::vector views; + + class Item { + char *desc; + Item(const Item &i) { assert(false); } + public: + std::string id; + std::string key; + std::string cmd; + const char *getText() const { return desc; } + void setText(const char *desc) { + if(this->desc) delete [] this->desc; + this->desc=new char[strlen(desc)+1]; + strcpy(this->desc,desc); + } + Item(const char *id, const char *key, const char *desc, const char *cmd) : + id(id), key(key), cmd(cmd) { this->desc=NULL; setText(desc); } + ~Item() { delete [] desc; } + }; + + std::vector items; + + typedef std::vector::iterator ItemIterator; + struct has_id : public std::binary_function { + bool operator ()( Item *item , const char *id) const { return item->id==id; } + }; + + int m_selection; + +public: + + enum notify_id { TITLE, WELCOMETEXT, SELECTION }; + void redraw() { sendNotify(TITLE); sendNotify(WELCOMETEXT); sendNotify(SELECTION); } + void setTitle(const char *s) { m_title = s; sendNotify(TITLE); } + const char*title() const { return m_title.c_str(); } + void setWelcomeText(const char *s) { m_welcomeText = s; sendNotify(WELCOMETEXT); } + const char *welcomeText() const { return m_welcomeText.c_str(); } + void setItem(const char *id, const char *sortkey, const char *desc, const char *cmd); + void delItem(const char *id); + int selection() const { return m_selection; } + bool haveSelection() const { return m_selection >= 0; } + void selectItem(const char *id); + const char *selectedCommand() const { return haveSelection() ? items[m_selection]->cmd.c_str() : ""; } + void cursorUp() { if( m_selection>0) m_selection--; sendNotify(SELECTION); } + void cursorDown() { if( m_selection+1items[index]->cmd.c_str() ); + printf("itemInserted %p %i \"%s\"\n", menu, index, text ); + } + virtual void itemChanged(Dynmenu *menu, int index, const char *text ) { + printf("itemChanged %p %i \"%s\"\n", menu, index, text ); + } + virtual ~DynmenuSimpleView() {} +}; + + +Dynmenu::ItemIterator Dynmenu::findItem(const char *id) +{ + using namespace std; + ItemIterator ret = find_if( items.begin(), items.end(), bind2nd( has_id(), id ) ); + return ret; +} + +void Dynmenu::setItem(const char *id, const char *sortkey, const char *desc, const char *cmd) +{ + std::vector::iterator i; + i = findItem(id); + if(i==items.end()) { + items.push_back( new Item(id,sortkey,desc,cmd) ); + // TODO: sort + sendItemInsertion( items.size()-1, items.back()->getText() ); + if(m_selection==-1) { + m_selection=items.size()-1; + sendNotify(SELECTION); + } + } + else { + (*i)->cmd = cmd; + (*i)->setText(desc); + (*i)->key = sortkey; // TODO: resort + sendItemChanged( i - items.begin(), (*i)->getText() ); + } +} + +void Dynmenu::selectItem(const char *id) +{ + std::vector::iterator i; + i = findItem(id); + if(i!=items.end()) { + m_selection = i - items.begin(); + sendNotify(SELECTION); + } +} + +void Dynmenu::delItem(const char *id){} + +void Dynmenu::registerView(DynmenuViewRef view) +{ + view->notify(this,TITLE); + view->notify(this,WELCOMETEXT); + std::vector::iterator i; + for(i=items.begin(); i!=items.end(); i++ ) { + view->itemInserted( this, i - items.begin(), (*i)->getText() ); + } + view->notify(this,SELECTION); + views.push_back(view); +} + +void Dynmenu::sendNotify(notify_id id) +{ + for( std::vector::iterator i=views.begin(); i!=views.end(); i++ ) { + (*i)->notify(this, id); + } +} +void Dynmenu::sendItemInsertion(int idx, const char *text ) +{ + for( std::vector::iterator i=views.begin(); i!=views.end(); i++ ) { + (*i)->itemInserted(this, idx, text); + } +} +void Dynmenu::sendItemChanged(int idx, const char *text ) +{ + for( std::vector::iterator i=views.begin(); i!=views.end(); i++ ) { + (*i)->itemChanged(this, idx, text); + } +} + +int _g_y = 0; + +#define kdprintf(fmt,...) \ + { char buf[200]; _g_y++; sprintf( buf, fmt "\n", ## __VA_ARGS__); mvaddnstr(_g_y,1,buf,strlen(buf)); refresh(); } +#define dprintf(fmt,...) + + +struct TextBox +{ + int y,x,w,h; + TextBox() : y(0),x(0),w(0),h(0) {} + void update(int x_, int y_, int w_, int h_) { y=y_;x=x_;w=w_;h=h_; } +}; + + +class LineDrawer +{ +public: + virtual void drawLine(WINDOW *win, int width, int number) = 0; + virtual int size() const = 0; +}; + +class ScrollWindow +{ + WINDOW *win; + int top_index; + LineDrawer *drawer; + void getExtants(int &maxy, int &maxx, int &count); +public: + void redraw(); + ScrollWindow() : win(NULL), top_index(0), drawer(NULL) {} + void setWin(WINDOW *w) { win=w; redraw(); } + void setLineDrawer(LineDrawer *d) { drawer = d; redraw(); } + void doScroll(int delta); + void scrollTo(int start, int end); + void invalidate(int start, int end); + void changedSize(); +}; + +void ScrollWindow::getExtants(int &maxy, int &maxx, int &count) +{ + getmaxyx(win,maxy,maxx); + count = drawer->size(); +} + +void ScrollWindow::redraw() +{ + if(!win || !drawer) return; + int maxy,maxx,count; + getExtants(maxy,maxx,count); + wborder(win, 0, 0, top_index>0 ? '.' : 0, count < top_index+maxy-1 ? 0 : '.' , 0, 0, 0, 0); + for(int y=1,i=top_index;ydrawLine(win,maxx-2,i++); + } + } + wrefresh(win); +} + +void ScrollWindow::invalidate(int start, int end) +{ + int maxy,maxx,count; + if( end <= top_index ) return; + getExtants(maxy,maxx,count); + if( start > top_index+maxy-2 ) return; + + if(start < top_index ) start = top_index; + if(end > top_index+maxy-2 ) end = top_index+maxy-2; + //if(end > count ) end = count; + + + int starty = 1 + start - top_index; + int stopy = starty + end - start; + + for(int y=starty,i=start;ydrawLine(win,maxx-2,i++); + //} + } + wrefresh(win); +} + +void ScrollWindow::scrollTo(int start, int end) +{ + if( start < top_index ) doScroll(start-top_index); + int maxy,maxx,count; + getExtants(maxy,maxx,count); + if( end > top_index+maxy-2 && start>top_index) { + int delta=end-top_index-maxy+2; + if( top_index+delta > start ) delta = start - top_index; + doScroll(delta); + } +} + +void ScrollWindow::changedSize() +{ + int maxy,maxx,count; + getExtants(maxy,maxx,count); + wmove(win,maxy-1,1); + if( top_index+maxy-2=-1 && delta<=1); + int maxy,maxx,count; + getExtants(maxy,maxx,count); + + if( top_index+delta<0 ) delta = -top_index; + if (delta>0 && top_index>0 && top_index+maxy-1+delta>count) delta=count-top_index-maxy+2; + if (top_index+delta>count) delta=0; + + if(delta==0) return; + dprintf("scrolling win=%p, drawer=%p, top_index+delta=%i, v=%i, count=%i", win, drawer, top_index+delta, top_index+maxy-1+delta, count); + + dprintf("scrolling %i", delta); + + WINDOW *w = derwin(win, maxy-2,maxx-2,1,1); + scrollok(w,true); + wscrl(w,delta); + scrollok(w,false); + touchwin(win); + + if( top_index==0 ) { + wmove(win,0,1); + whline(win,'.',maxx-2); + } + if( top_index+maxy-2==count) { + wmove(win,maxy-1,1); + whline(win,'.',maxx-2); + } + top_index += delta; + int y = delta<0 ? 0 : maxy-2-delta; + int i = y + top_index; + dprintf("scroll values i=%i, y=%i", i, y); + delta = delta<0 ? -delta : delta; + for(int j=0;jdrawLine(w,maxx-2,i++); + + } + wrefresh(w); + delwin(w); + if( top_index==0 ) { + wmove(win,0,1); + whline(win,0,maxx-2); + } + if( top_index+maxy-2==count) { + wmove(win,maxy-1,1); + whline(win,0,maxx-2); + } + wrefresh(win); +} + +/* +class ItemDrawer : public LineDrawer +{ +public: + void drawLine(WINDOW *win, int width, int number); + int size() const { return 50; } +}; + +void ItemDrawer::drawLine(WINDOW *win, int width, int number) +{ + char buf[200]; + sprintf(buf, "This is line #%i.", number ); + waddnstr(win,buf, width); +} +*/ + +class DynmenuCursesView : public DynmenuView, public LineDrawer +{ + int maxx, maxy; + WINDOW *win; + TextBox titleBox; + TextBox welcomeBox; + WINDOW *itemwin; + ScrollWindow itemScroll; + struct Item { + int top; + std::vector text; + int compare(int line) { + if(line=top+text.size()) return 1; + else return 0; + } + }; + std::vector items; + int cached_index; + int selected; +public: + DynmenuCursesView() : win(NULL),itemwin(NULL), maxx(0), maxy(0), cached_index(0), selected(-1) { + itemScroll.setLineDrawer(this); + } + ~DynmenuCursesView() { delwin(itemwin); } + void show(WINDOW *win); + void notify(Dynmenu *menu, Dynmenu::notify_id id); + void itemInserted(Dynmenu *menu, int index, const char *text ); + void itemChanged(Dynmenu *menu, int index, const char *text ); + + // LineDrawer interface + void drawLine(WINDOW *win, int width, int number); + int size() const { if(items.size()==0) return 0; else return items.back().top + items.back().text.size(); } +}; + +void DynmenuCursesView::drawLine(WINDOW *win, int width, int number ) +{ + int i = cached_index; + while( items[i].compare(number)>0 && i0 ) i--; + if( items[i].compare(number)!=0 ) { + // If out of range, clear row + wattroff(win, A_STANDOUT); + for(int i=0;iwidth ? width : len; + // TODO: draw selection + if( selected == cached_index ) + wattron(win, A_STANDOUT); + else + wattroff(win, A_STANDOUT); + waddnstr( win, item.text[i], len ); + int mright = width - len; + for(int j=0;j::iterator item = items.insert( items.begin() + index, Item() ); + const char *p = text; + item->text.push_back(p); + for(;;) { + while( *p && *p!='\n' ) p++; + if(*p) p++; + if(*p) item->text.push_back(p); + else break; + } + int firstrow = 0; + if(item!=items.begin()) { + item->top = (item-1)->top + (item-1)->text.size(); + } + else + item->top = 0; + + firstrow = item->top; + if((item+1)!=items.end()) { + size_t newrows = item->text.size(); + for( item++; item!=items.end(); item++ ) item->top += newrows; + } + itemScroll.invalidate( firstrow, size() ); + itemScroll.changedSize(); + wrefresh(win); +} + +void DynmenuCursesView::itemChanged(Dynmenu *menu, int index, const char *text) +{ + std::vector::iterator item = items.begin() + index; + size_t oldsize = item->text.size(); + int lastrow = items.back().top + items.back().text.size(); + item->text.clear(); + const char *p = text; + item->text.push_back(p); + for(;;) { + while( *p && *p!='\n' ) p++; + if(*p) p++; + if(*p) item->text.push_back(p); + else break; + } + int firstrow = item->top; + int diff = item->text.size() - oldsize; + if( diff ) { + for( item ++; item!=items.end(); item++ ) item->top += diff; + item = items.end() - 1; + } + else lastrow = item->top + oldsize; + if( lastrow < item->top + item->text.size() ) lastrow = item->top + item->text.size(); + + itemScroll.invalidate( firstrow, lastrow ); + itemScroll.changedSize(); + wrefresh(win); +} + +void getTextExtents(const char *text, int &width, int &height ) +{ + width = 0; + height = 0; + const char *p; + const char *s; + p = text; + do { + s = p; + while( *p && *p!='\n' ) p++; + if(p-s>0) { + height++; + if(widthtitle(), tw, th ); + tx = 0; + ty = 0; + tw = maxx - tx; + dprintf("tx = %i, ty=%i, title=%s, tw=%i", tx, ty, menu->title(), tw ); + clearBox(win,titleBox); + oldbottom = titleBox.y+titleBox.h; + titleBox.update(tx,ty,tw,th); + if(oldbottom!=titleBox.y+titleBox.h) { + notify(menu,Dynmenu::WELCOMETEXT); + } + drawText(win,titleBox, menu->title(), true ); + break; + case Dynmenu::WELCOMETEXT: + getTextExtents( menu->welcomeText(), tw, th ); + tx = ( maxx - tw )/ 2; + ty = titleBox.y+titleBox.h+2; + clearBox(win,welcomeBox); + if(ty+th!=welcomeBox.y+welcomeBox.h) { + // Need to resize itemsBox + if(itemwin) { + werase(itemwin); + delwin(itemwin); + } + itemwin = derwin(win,maxy-ty-th-1,maxx-3, ty+th, 2); + itemScroll.setWin(itemwin); + } + welcomeBox.update(tx,ty,tw,th); + drawText(win,welcomeBox, menu->welcomeText() ); + //mvwaddstr(win,ty,tx, menu->welcomeText() ); + break; + case Dynmenu::SELECTION: + //dprintf("items=%zu, selection=%i", menu->items.size(), menu->selection() ); + int sel=selected; + selected = menu->selection(); + if(sel!=-1) + itemScroll.invalidate(items[sel].top, items[sel].top+items[sel].text.size() ); + sel = selected; + if(sel!=-1) { + itemScroll.invalidate(items[sel].top, items[sel].top+items[sel].text.size() ); + itemScroll.scrollTo(items[sel].top, items[sel].top+items[sel].text.size() ); + wrefresh(itemwin); + } + break; + } + wrefresh(win); +} + +void DynmenuCursesView::show(WINDOW *win) +{ + if( win ) { + this->win = win; + getmaxyx(win,maxy,maxx); + //box(win, 0, 0); + if(itemwin) { wrefresh(itemwin); } + wrefresh(win); + } + else { + } +} + +#include +#include + +using std::string; +using std::isspace; + +class Parser +{ + int peeked; + void *gdata; + int (*getter)(void *); + int peek() { return peeked==-2 ? (peeked=getter(gdata)) : peeked; } + int get() { int retv = peeked==-2? (peeked=getter(gdata)) : peeked; peeked=-2; return retv;} + bool peekedSpace() { return isspace(peek()); } + void skipSpace() { while(peekedSpace()) get(); } +public: + enum tokentype { Identifier, String, EOL }; + enum exception { NoGetter, SyntaxError }; + void setCharGetter( int (*g)(void*), void *user ) { getter=g; gdata=user; } + void expect( tokentype t, std::string &s ); + Parser() : peeked(-2), getter(NULL), gdata(NULL) {} +}; + +void Parser::expect( tokentype t, std::string &s ) +{ + if(! getter ) throw NoGetter; + + int c; + + switch( t ) { + case Identifier: + skipSpace(); + s = ""; + while( peek()!=-1 && ! peekedSpace() ) s += (char)get() ; + break; + case String: + skipSpace(); + s = ""; + if( peek() !='"' ) throw SyntaxError; + get(); // peel off open-quote + while(peek()!='"' && peek()!=-1 ) { + c = get(); + if( c=='\\') { + c=get(); // allow escaped quotes + if(c=='n') c='\n'; + else if(c=='t') c='\t'; + } + s += (char)c ; + } + get(); // peel off close-quote + break; + case EOL: + while( peek()!=-1 && isspace(peek()) && peek()!='\n' ) get(); + if( !isspace(peek()) ) throw SyntaxError; + break; + } +} + + + + +#include +#include +#include +#include + +#define CTRLL 12 + + +int buf_getc( const char **p ) +{ + if(**p) return *((*p)++); + else return -1; +} + +void trap_signals() +{ + struct sigaction s; + s.sa_handler = SIG_IGN; + int sig[] = { SIGINT, SIGTSTP, SIGQUIT, }; + for (int i=0; i= 0) + { + if (FD_ISSET(0, &fds)) { + //dprintf("char !");//space!"); + int c = getch(); + if (c == ERR) { + perror("error reading stdin"); + return 1; + } + switch (c) { + case KEY_UP: case 'k': menu.cursorUp(); break; + case KEY_DOWN: case 'j': menu.cursorDown(); break; + case KEY_RIGHT: case '\n': case '\x0d': case 'l': + case KEY_ENTER: + if (!menu.haveSelection()) + break; + bye(); + endwin(); + execl("/bin/sh", "/bin/sh", "-c", menu.selectedCommand(), (char *) NULL); + perror("exec failed"); + exit(1); + break; + + case 'Q': bye(); endwin(); exit(0); break; + +#ifndef SIMPLE + case 'r': + case CTRLL: menu.redraw(); break; +#endif +/* + case ' ': + { + dprintf("space!"); + static char b = 'A'; + char buf[80]; sprintf( buf, "Letter '%c'", b++ ); + menu.setItem( "id3", "3", buf, "id3 cmd"); + } +*/ + break; + } + } + if (FD_ISSET(fd, &fds)) { + Parser *p = new Parser; + char line[1024];//[PIPE_BUF]; + assert(line); + char *pointer = line; + while (fgets(line, 1024, fifo) != NULL) { + dprintf("%s", line ); + try { + pointer = line; + string menuMethod(1024,' '); + string id(1024, ' '); string sortkey(1024, ' '); + string desc(1024, ' '); string cmd(1024, ' '); string dummy; + p->setCharGetter( (int (*)(void*))buf_getc, (void*)&pointer ); + p->expect( Parser::Identifier, menuMethod ); + if( menuMethod=="setItem" ) { + p->expect( Parser::String, id ); + p->expect( Parser::String, sortkey ); + p->expect( Parser::String, desc ); + p->expect( Parser::String, cmd ); + menu.setItem( id.c_str(), sortkey.c_str(), desc.c_str(), cmd.c_str() ); + } + else if( menuMethod=="setTitle" ) { + p->expect( Parser::String, desc ); + menu.setTitle( desc.c_str() ); + } + else if( menuMethod=="setWelcomeText" ) { + p->expect( Parser::String, desc ); + menu.setWelcomeText( desc.c_str() ); + } + else if( menuMethod=="delItem" ) { + p->expect( Parser::String, id ); + menu.delItem( id.c_str() ); + } + else if( menuMethod=="selectItem" ) { + p->expect( Parser::String, id ); + menu.selectItem( id.c_str() ); + } + } + catch(...) { + dprintf("exception"); + } + } + } + FD_ZERO(&fds); + if (!feof(fifo)) // will never eof if it is a fifo + FD_SET(fd, &fds); // could use inotify here... + FD_SET(0, &fds); + } + char ebuf[80] =""; + switch(errno) + { + case EINTR: + menu.redraw(); + goto top; + case EBADF: strcpy(ebuf, "EBADF"); break; + case EINVAL: strcpy(ebuf, "EINVAL"); break; + case ENOMEM: strcpy(ebuf, "ENOMEM"); break; + default: sprintf(ebuf, "0x%X", errno); + } + endwin(); + printf(" errno = %s\n", ebuf ); + + return 0; +} + +// vim:ts=2 sw=2 et: -- cgit v1.2.3