Note: Goby version 1 (shown here) is now considered obsolete. Please use version 2 for new projects, and consider upgrading old projects.

Goby Underwater Autonomy Project  Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
util/liblogger/flex_ncurses.cpp
00001 // copyright 2009 t. schneider tes@mit.edu
00002 //
00003 // this file is part of flex-ostream, a terminal display library
00004 // that provides an ostream with both terminal display and file logging
00005 //
00006 // This program is free software: you can redistribute it and/or modify
00007 // it under the terms of the GNU General Public License as published by
00008 // the Free Software Foundation, either version 3 of the License, or
00009 // (at your option) any later version.
00010 //
00011 // This software is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU General Public License
00017 // along with this software.  If not, see <http://www.gnu.org/licenses/>.
00018 
00019 #include <cmath>
00020 #include <iostream>
00021 #include <algorithm>
00022 #include <sstream>
00023 
00024 #include <ncurses.h>
00025 #include <boost/foreach.hpp>
00026 #include <boost/algorithm/string.hpp>
00027 
00028 #include "goby/util/string.h"
00029 
00030 #include "logger_manipulators.h"
00031 
00032 #include "flex_ncurses.h"
00033 #include "term_color.h"
00034 
00035 using boost::posix_time::ptime;
00036 
00037 // defined in flex_ostreambuf.cpp
00038 extern boost::mutex curses_mutex;
00039 
00040 
00041 goby::util::FlexNCurses::FlexNCurses()
00042     : xmax_(0),
00043       ymax_(0),
00044       xwinN_(1),
00045       ywinN_(1),
00046       foot_window_(0),
00047       is_locked_(false),
00048       locked_panel_(0),
00049       alive_(true)
00050 {  }
00051 
00052 
00053 void goby::util::FlexNCurses::startup()
00054 {
00055     initscr();
00056     start_color();
00057     
00058     
00059     init_pair(Colors::white, COLOR_WHITE, COLOR_BLACK);
00060     init_pair(Colors::red, COLOR_RED, COLOR_BLACK);
00061     init_pair(Colors::green, COLOR_GREEN, COLOR_BLACK);
00062     init_pair(Colors::yellow, COLOR_YELLOW, COLOR_BLACK);
00063     init_pair(Colors::blue, COLOR_BLUE, COLOR_BLACK);
00064     init_pair(Colors::magenta, COLOR_MAGENTA, COLOR_BLACK);
00065     init_pair(Colors::cyan, COLOR_CYAN, COLOR_BLACK);
00066     
00067     refresh();
00068     update_size();
00069     // special keys (KEY_RESIZE, KEY_LEFT, etc.)
00070     keypad(stdscr, TRUE);
00071     // no cursor
00072     curs_set(false);
00073     // tenths of seconds pause waiting for character input (getch)
00074     nodelay(stdscr, TRUE);
00075     halfdelay(1);
00076     noecho();
00077     // turn on mouse events
00078 
00079     // problems with locking screen
00080 //    mousemask(ALL_MOUSE_EVENTS, 0);
00081 }
00082 
00083 void goby::util::FlexNCurses::update_size()
00084 {
00085     getmaxyx(stdscr, ymax_, xmax_);
00086     ymax_ -= FOOTER_Y;
00087     
00088     xwinN_ = std::max(1, xmax_ / CHARS_PER_LINE );
00089     ywinN_ = (panels_.size() % xwinN_) ? panels_.size() / xwinN_ + 1 : panels_.size() / xwinN_;
00090 
00091     line_buffer_.resize(xwinN_);
00092     BOOST_FOREACH(size_t i, unique_panels_)
00093     {
00094         int col = (i / ywinN_);
00095         panels_[i].col(col);
00096         panels_[i].minimized(false);
00097         panels_[i].ywidth(ymax_ / ywinN_);
00098         line_buffer_[col] = ymax_;
00099         //int ywidth = (m_N == ywinN_) ? 0 : (ymax_-m_N*HEAD_Y)/(ywinN_-m_N);
00100     }
00101 
00102     BOOST_FOREACH(size_t i, unique_panels_)
00103     {
00104         int col = (i / ywinN_);
00105         line_buffer_[col] -= panels_[i].ywidth();
00106     }
00107 }
00108 
00109 void goby::util::FlexNCurses::alive(bool alive) { alive_ = alive; }
00110 void goby::util::FlexNCurses::cleanup() { endwin(); }
00111 
00112 void goby::util::FlexNCurses::add_win(const Group* g)
00113 {
00114     int N_windows = panels_.size()+1;
00115         
00116     if(xwinN_*ywinN_ < N_windows)
00117         ++ywinN_;
00118 
00119     Panel new_panel = Panel(g);
00120     
00121     new_panel.original_order(panels_.size());
00122     unique_panels_.insert(panels_.size());
00123     panels_.push_back(new_panel);
00124     
00125     update_size();
00126     recalculate_win();
00127 }
00128 
00129 void goby::util::FlexNCurses::recalculate_win()
00130 {
00131     
00132     // clear any old windows
00133     BOOST_FOREACH(size_t i, unique_panels_)
00134     {
00135         delwin(static_cast<WINDOW*>(panels_[i].window()));
00136         delwin(static_cast<WINDOW*>(panels_[i].head_window()));
00137         panels_[i].window(0);
00138         panels_[i].head_window(0);
00139     }    
00140     BOOST_FOREACH(void* p, vert_windows_)
00141     {
00142         delwin(static_cast<WINDOW*>(p));
00143         p = 0;
00144     }
00145     BOOST_FOREACH(void* p, col_end_windows_)
00146     {
00147         delwin(static_cast<WINDOW*>(p));
00148         p = 0;
00149     }
00150     delwin(static_cast<WINDOW*>(foot_window_));    
00151     
00152     
00153     redrawwin(stdscr);
00154     refresh();
00155 
00156     foot_window_ = newwin(FOOTER_Y-1, xmax_, ymax_+1, 0);
00157     mvwaddstr(static_cast<WINDOW*>(foot_window_), 0, 0, "help: [+]/[-]: expand/contract window | [w][a][s][d]: move window | spacebar: toggle minimize | [r]: reset | [CTRL][A]: select all | [1][2][3]...[n] select window n | [SHIFT][[n] select multiple | [enter] pause and scroll | [c]/[C] combine/uncombine selected windows");
00158     wrefresh(static_cast<WINDOW*>(foot_window_));
00159             
00160     col_end_windows_.resize(xwinN_);
00161     vert_windows_.resize(xwinN_-1);
00162     BOOST_FOREACH(size_t i, unique_panels_)
00163     {
00164         int col = panels_[i].col();
00165         int ystart = 0;
00166 
00167         
00168         for(std::set<size_t>::iterator it = unique_panels_.begin(); (*it) < i; ++it)
00169         {
00170             if(panels_[*it].col() == col)
00171                 ystart += panels_[*it].ywidth();
00172         }
00173 
00174         int ywidth = panels_[i].ywidth();
00175         int xwidth = xmax_ / xwinN_;
00176         int xstart = col*xwidth;
00177         
00178         // vertical divider
00179         // not last column, not if only one column
00180         if(col < xwinN_-1 && xwinN_ > 1)
00181         {
00182             WINDOW* new_vert = newwin(ymax_, 1, 0, (col+1)*xwidth-1);
00183             mvwvline(new_vert, 0, 0, 0, ymax_);
00184             wrefresh(new_vert);
00185             vert_windows_[col] = new_vert;
00186             --xwidth;
00187         }
00188 
00189         if(last_in_col(i))
00190         {
00191             WINDOW* new_col_end = newwin(1, xwidth, ystart+ywidth, xstart);
00192             col_end_windows_[col] = new_col_end;
00193             mvwhline(new_col_end, 0, 0, 0, xwidth);
00194             wrefresh(new_col_end);
00195         }
00196         
00197         // title and horizontal divider
00198         WINDOW* new_head = newwin(HEAD_Y, xwidth, ystart, xstart);
00199         panels_[i].head_window(new_head);
00200         BOOST_FOREACH(size_t j, panels_[i].combined())
00201             panels_[j].head_window(new_head);
00202 
00203         write_head_title(i);
00204         
00205         if(panels_[i].selected()) select(i);
00206 
00207         if(ywidth > HEAD_Y)
00208         {            
00209             // scrolling text window
00210             WINDOW* new_window = newwin(ywidth-HEAD_Y, xwidth, ystart+HEAD_Y, xstart);
00211             panels_[i].window(new_window);
00212             BOOST_FOREACH(size_t j, panels_[i].combined())
00213                 panels_[j].window(new_window);            
00214             
00215             
00216             // move the cursor to start place for text
00217             wmove(new_window, 0, 0);
00218 
00219             // set the background color
00220             wbkgd(new_window, COLOR_PAIR(Colors::white));
00221 
00222             // scrolling is good!
00223             scrollok(new_window, true);
00224 
00225             redraw_lines(i);
00226             
00227             
00228             // make it all stick
00229             wrefresh(new_window);
00230         }        
00231     }    
00232 }
00233 
00234 void goby::util::FlexNCurses::insert(ptime t, const std::string& s, Group* g)
00235 {
00236     size_t i = panel_from_group(g);
00237     
00238     std::multimap<ptime, std::string>& hist = panels_[i].history();
00239     if(hist.size() >= panels_[i].max_hist())
00240         hist.erase(hist.begin());
00241     
00242 
00243     //  first line shouldn't have newline at the start...
00244     if(!panels_[i].locked())
00245     {
00246         //  first line shouldn't have newline at the start...
00247         if(hist.empty())
00248             putline(boost::trim_left_copy(s), i);
00249         else
00250             putline(s, i);
00251     }
00252     hist.insert(std::pair<ptime, std::string>(t, s));
00253 }
00254 
00255 size_t goby::util::FlexNCurses::panel_from_group(Group* g)
00256 {
00257 //    BOOST_FOREACH(size_t i, unique_panels_)
00258     for(size_t i = 0, n = panels_.size(); i < n; ++i)
00259     {
00260         if(panels_[i].group() == g) return i;
00261     }
00262     return 0;
00263 }
00264 
00265 void goby::util::FlexNCurses::putline(const std::string &s, unsigned scrn, bool refresh /* = true */)
00266 {
00267     if(s.empty())
00268         return;
00269     
00270     if(scrn < panels_.size())
00271     {
00272         WINDOW* win = static_cast<WINDOW*>(panels_[scrn].window());
00273         if(!win) return;
00274 
00275         static const std::string esc = "\33[";
00276         static const std::string m = "m";
00277         
00278         size_t esc_pos = -1, m_pos = -1, last_m_pos = -1;
00279         size_t length = s.length();
00280         while((esc_pos = s.find(esc, esc_pos+1)) != std::string::npos
00281               && (m_pos = s.find(m, esc_pos)) != std::string::npos)
00282         {
00283             std::string esc_sequence = s.substr(esc_pos, m_pos-esc_pos+1);
00284 
00285             if(last_m_pos >= 0)
00286                 waddstr(win, s.substr(last_m_pos+1, esc_pos-last_m_pos-1).c_str());
00287 
00288             // void kills compiler warning and doesn't do anything bad
00289             (void) wattrset(win, color2attr_t(TermColor::from_esc_code(esc_sequence)));
00290             
00291             last_m_pos = m_pos;            
00292         }
00293 
00294         if(last_m_pos+1 < length)
00295             waddstr(win, s.substr(last_m_pos+1).c_str());
00296 
00297         if(refresh)
00298             wrefresh(win);
00299     }
00300     
00301 }
00302 
00303 
00304 void goby::util::FlexNCurses::putlines(unsigned scrn,
00305                            const std::multimap<ptime, std::string>::const_iterator& alpha,
00306                            const std::multimap<ptime, std::string>::const_iterator& omega,
00307                            bool refresh /* = true */)
00308 {
00309     for(std::multimap<ptime, std::string>::const_iterator it = alpha; it != omega; ++it)
00310     {
00311         if(it == alpha)
00312             putline(boost::trim_left_copy(it->second), scrn, false);
00313         else
00314             putline(it->second, scrn, false);
00315         
00316     }
00317     
00318         
00319     if(refresh)
00320         wrefresh(static_cast<WINDOW*>(panels_[scrn].window()));
00321 
00322 }
00323 
00324 
00325 long goby::util::FlexNCurses::color2attr_t(Colors::Color c)
00326 {
00327     switch(c)
00328     {
00329         default:
00330         case Colors::nocolor:    return COLOR_PAIR(Colors::white);
00331         case Colors::red:        return COLOR_PAIR(Colors::red);
00332         case Colors::lt_red:     return COLOR_PAIR(Colors::red) | A_BOLD;
00333         case Colors::green:      return COLOR_PAIR(Colors::green);
00334         case Colors::lt_green:   return COLOR_PAIR(Colors::green) | A_BOLD;
00335         case Colors::yellow:     return COLOR_PAIR(Colors::yellow);
00336         case Colors::lt_yellow:  return COLOR_PAIR(Colors::yellow) | A_BOLD;
00337         case Colors::blue:       return COLOR_PAIR(Colors::blue);
00338         case Colors::lt_blue:    return COLOR_PAIR(Colors::blue) | A_BOLD;
00339         case Colors::magenta:    return COLOR_PAIR(Colors::magenta);
00340         case Colors::lt_magenta: return COLOR_PAIR(Colors::magenta) | A_BOLD;
00341         case Colors::cyan:       return COLOR_PAIR(Colors::cyan);
00342         case Colors::lt_cyan:    return COLOR_PAIR(Colors::cyan) | A_BOLD;
00343         case Colors::white:      return COLOR_PAIR(Colors::white);
00344         case Colors::lt_white:   return COLOR_PAIR(Colors::white) | A_BOLD;
00345     }
00346     
00347 }
00348 
00349 // find screen containing click
00350 size_t goby::util::FlexNCurses::find_containing_window(int y, int x)
00351 {
00352     BOOST_FOREACH(size_t i, unique_panels_)
00353     {
00354         if(in_window(panels_[i].window(),y,x) || in_window(panels_[i].head_window(),y,x))
00355             return i;
00356     }
00357     return panels_.size();
00358 }
00359 
00360 bool goby::util::FlexNCurses::in_window(void* p, int y, int x)
00361 {
00362     if(!p) return false;
00363     
00364     int ybeg, xbeg;
00365     int ymax, xmax;
00366     getbegyx(static_cast<WINDOW*>(p), ybeg, xbeg);    
00367     getmaxyx(static_cast<WINDOW*>(p), ymax, xmax);    
00368 
00369     return (y < ybeg+ymax && y >= ybeg && x < xbeg+xmax && x >= xbeg);
00370 }
00371 
00372 
00373 void goby::util::FlexNCurses::write_head_title(size_t i)
00374 {
00375     WINDOW* win = static_cast<WINDOW*>(panels_[i].head_window());
00376 
00377     (void) wattrset(win, color2attr_t(util::Colors::lt_white));
00378         
00379     int ymax, xmax;
00380     getmaxyx(win, ymax, xmax);
00381     mvwhline(win, 0, 0, 0, xmax);
00382     
00383     if(panels_[i].selected())
00384         wattron(win, A_REVERSE);
00385     else
00386         wattroff(win, A_REVERSE);
00387 
00388     
00389 
00390 
00391     attr_t color_attr = color2attr_t(panels_[i].group()->color());
00392     attr_t white_attr = color2attr_t(Colors::lt_white);
00393     wattron(win, white_attr);
00394     mvwaddstr(win, 0, 0, std::string(as<std::string>(i+1)+". ").c_str());
00395 
00396 
00397     
00398     
00399     std::stringstream ss;
00400     ss << panels_[i].group()->description() << " ";    
00401     wattron(win, color_attr);
00402     waddstr(win, ss.str().c_str());
00403     
00404     BOOST_FOREACH(size_t j, panels_[i].combined())
00405     {
00406         wattron(win, white_attr);
00407         waddstr(win, "| ");
00408         color_attr = color2attr_t(panels_[j].group()->color());
00409         waddstr(win, std::string(as<std::string>(j+1)+". ").c_str());
00410               
00411         std::stringstream ss_com;
00412         ss_com << panels_[j].group()->description() << " ";
00413         wattron(win, color_attr);
00414         waddstr(win, ss_com.str().c_str());
00415     }
00416 
00417     std::stringstream ss_tags;
00418     if(panels_[i].minimized())
00419         ss_tags << "(minimized) ";
00420     if(panels_[i].locked())
00421         ss_tags << "(paused, hit return to unlock) ";
00422     wattron(win, white_attr);
00423     waddstr(win, ss_tags.str().c_str());
00424     
00425 
00426     wrefresh(win);
00427 }
00428 
00429 void goby::util::FlexNCurses::deselect_all()
00430 {
00431     if(is_locked_)
00432         return;
00433 
00434     BOOST_FOREACH(size_t i, unique_panels_)
00435     {
00436         if(panels_[i].selected())
00437         {
00438             panels_[i].selected(false);
00439             write_head_title(i);
00440         }    
00441     }
00442 }
00443 
00444 void goby::util::FlexNCurses::select_all()
00445 {
00446     BOOST_FOREACH(size_t i, unique_panels_)
00447         select(i);
00448 }
00449 
00450 
00451 void goby::util::FlexNCurses::select(size_t gt)
00452 {    
00453     if(is_locked_)
00454         return;
00455     
00456     if(gt < panels_.size())
00457     {
00458         panels_[gt].selected(true);
00459         write_head_title(gt);
00460     }    
00461 }
00462 
00463 size_t goby::util::FlexNCurses::down(size_t curr)
00464 {
00465     int ybeg, xbeg;
00466     int ymax, xmax;
00467     getbegyx(static_cast<WINDOW*>(panels_[curr].head_window()), ybeg, xbeg);
00468     getmaxyx(static_cast<WINDOW*>(panels_[curr].window()), ymax, xmax);
00469     size_t next = find_containing_window(ybeg+ymax+HEAD_Y+1, xbeg);
00470 
00471     return next;
00472 }
00473 
00474 size_t goby::util::FlexNCurses::up(size_t curr)
00475 {
00476     int ybeg, xbeg;
00477     getbegyx(static_cast<WINDOW*>(panels_[curr].head_window()), ybeg, xbeg);
00478     size_t next = find_containing_window(ybeg-1, xbeg);
00479     return next;
00480 }
00481 
00482 size_t goby::util::FlexNCurses::left(size_t curr)
00483 {
00484     int ybeg, xbeg;
00485     getbegyx(static_cast<WINDOW*>(panels_[curr].head_window()), ybeg, xbeg);
00486     // cross the divider
00487     size_t next = find_containing_window(ybeg, xbeg-2);
00488     return next;
00489 }
00490 
00491 size_t goby::util::FlexNCurses::right(size_t curr)
00492 {
00493     int ybeg, xbeg;
00494     int ymax, xmax;
00495     getbegyx(static_cast<WINDOW*>(panels_[curr].head_window()), ybeg, xbeg);
00496     getmaxyx(static_cast<WINDOW*>(panels_[curr].head_window()), ymax, xmax);
00497     // cross the divider
00498     size_t next = find_containing_window(ybeg, xbeg+xmax+2);
00499     return next;
00500 }
00501 
00502 void goby::util::FlexNCurses::home()
00503 {
00504     shift(0);
00505 }
00506 
00507 void goby::util::FlexNCurses::end()
00508 {
00509     shift(panels_.size()-1);
00510 }
00511 
00512 void goby::util::FlexNCurses::shift(size_t next)
00513 {
00514     if(next < panels_.size())
00515     {    
00516         deselect_all();
00517         select(next);
00518     }
00519 }
00520 
00521 void goby::util::FlexNCurses::combine()
00522 {
00523     size_t lowest;
00524     BOOST_FOREACH(size_t i, unique_panels_)
00525     {
00526         if(panels_[i].selected())
00527         {
00528             lowest = i;
00529             break;
00530         }
00531     }
00532 
00533     BOOST_FOREACH(size_t i, unique_panels_)
00534     {
00535         if(panels_[i].selected() && i != lowest)
00536         {
00537             panels_[lowest].add_combined(i);
00538             BOOST_FOREACH(size_t j, panels_[i].combined())
00539                 panels_[lowest].add_combined(j);
00540 
00541             panels_[i].clear_combined();
00542             
00543             unique_panels_.erase(i);
00544         }
00545     }
00546     
00547     update_size();
00548     recalculate_win();
00549 }
00550 
00551 void goby::util::FlexNCurses::uncombine(size_t i)
00552 {
00553     BOOST_FOREACH(size_t j, panels_[i].combined())
00554         unique_panels_.insert(j);
00555     panels_[i].clear_combined();
00556 }
00557 
00558 void goby::util::FlexNCurses::uncombine_selected()
00559 {
00560     BOOST_FOREACH(size_t i, unique_panels_)
00561     {
00562         if(panels_[i].selected())
00563         {
00564             uncombine(i);
00565         }
00566     }
00567 
00568     update_size();
00569     recalculate_win();
00570 }
00571 
00572 void goby::util::FlexNCurses::uncombine_all()
00573 {
00574     BOOST_FOREACH(size_t i, unique_panels_)
00575         uncombine(i);
00576 
00577     update_size();
00578     recalculate_win();
00579 }
00580 
00581 
00582 
00583 void goby::util::FlexNCurses::move_up()
00584 {
00585     BOOST_FOREACH(size_t i, unique_panels_)
00586     {
00587         if(!first_in_col(i) && panels_[i].selected())
00588             iter_swap(panels_.begin()+i, panels_.begin()+i-1);
00589     }
00590     recalculate_win();
00591 }
00592 
00593 void goby::util::FlexNCurses::move_down()
00594 { 
00595     //    BOOST_REVERSE_FOREACH(size_t i, unique_panels_)   
00596     for(std::set<size_t>::reverse_iterator it = unique_panels_.rbegin(),
00597             n = unique_panels_.rend(); it != n; ++it)
00598     {
00599         size_t i = *it;
00600         if(!last_in_col(i) && panels_[i].selected())
00601             iter_swap(panels_.begin()+i, panels_.begin()+i+1);
00602     }
00603     recalculate_win();
00604 }
00605 
00606 void goby::util::FlexNCurses::move_right()
00607 {
00608     //    BOOST_REVERSE_FOREACH(size_t i, unique_panels_)
00609     for(std::set<size_t>::reverse_iterator it = unique_panels_.rbegin(),
00610             n = unique_panels_.rend(); it != n; ++it)
00611     {
00612         size_t i = *it;
00613         size_t rpanel = right(i);
00614         if(rpanel == panels_.size())
00615         {
00616             BOOST_FOREACH(size_t j, unique_panels_)
00617             {
00618                 if(last_in_col(j) && panels_[i].col()+1 == panels_[j].col())
00619                     rpanel = j+1;
00620             }
00621         }
00622 
00623         int old_col = panels_[i].col();
00624         if(panels_[i].selected() && old_col < xwinN_-1)
00625         {
00626             panels_[i].col(old_col+1);
00627             Panel p = panels_[i];
00628             int orig_width = p.ywidth();
00629             line_buffer_[old_col] += orig_width;
00630             p.ywidth(0);
00631             panels_.insert(panels_.begin() + rpanel, p);
00632             panels_.erase(panels_.begin() + i);
00633             for(int j = 0, m = orig_width; j < m; ++j)
00634                 grow(rpanel-1);
00635         }
00636     }
00637     
00638     recalculate_win();
00639 }
00640 
00641 void goby::util::FlexNCurses::move_left()
00642 {
00643     BOOST_FOREACH(size_t i, unique_panels_)
00644     {
00645         size_t lpanel = left(i);
00646         if(lpanel == panels_.size())
00647         {    
00648             BOOST_FOREACH(size_t j, unique_panels_)
00649             {
00650                 if(first_in_col(j) && panels_[i].col() == panels_[j].col())
00651                     lpanel = j;
00652             }
00653         }
00654         
00655         int old_col = panels_[i].col();        
00656         if(panels_[i].selected() && old_col > 0)
00657         {   
00658             panels_[i].col(old_col-1);
00659             Panel p = panels_[i];            
00660             int orig_width = p.ywidth();
00661             line_buffer_[old_col] += orig_width;
00662             p.ywidth(0);
00663             panels_.erase(panels_.begin() + i);
00664             panels_.insert(panels_.begin() + lpanel, p);
00665 
00666             for(int j = 0, m = orig_width; j < m; ++j)
00667                 grow(lpanel);
00668         }
00669     }
00670     recalculate_win();
00671 }
00672 
00673 size_t goby::util::FlexNCurses::find_first_selected()
00674 {
00675     BOOST_FOREACH(size_t i, unique_panels_)
00676     {
00677         if(panels_[i].selected())
00678             return i;    
00679     }
00680     return 0;
00681 }
00682 
00683 bool goby::util::FlexNCurses::last_in_col(size_t val)
00684 {
00685     BOOST_FOREACH(size_t i, unique_panels_)
00686     {
00687         if(panels_[i].col() == panels_[val].col() && i > val)
00688             return false;
00689     }
00690     return true;
00691 }
00692 
00693 bool goby::util::FlexNCurses::first_in_col(size_t val)
00694 {
00695     BOOST_FOREACH(size_t i, unique_panels_)
00696     {
00697         if(panels_[i].col() == panels_[val].col() && i < val)
00698             return false;
00699     }
00700     return true;
00701 }
00702 
00703 void goby::util::FlexNCurses::grow_all()
00704 {
00705     BOOST_FOREACH(size_t i, unique_panels_)
00706     {
00707         if(panels_[i].selected())
00708             grow(i);
00709     }
00710     recalculate_win(); 
00711 }
00712 
00713 void goby::util::FlexNCurses::shrink_all()
00714 {
00715 
00716     BOOST_FOREACH(size_t i, unique_panels_)
00717     {
00718         if(panels_[i].selected())
00719             shrink(i);
00720     }
00721     recalculate_win(); 
00722 }
00723 
00724 
00725 void goby::util::FlexNCurses::grow(int i)
00726 {
00727     panels_[i].set_minimized(false);
00728     size_t largest_panel = panels_.size();
00729     int largest_panel_size = 0;
00730     BOOST_FOREACH(size_t j, unique_panels_)
00731     {
00732         if(panels_[j].ywidth() >= largest_panel_size && panels_[j].col() == panels_[i].col() && !panels_[j].minimized() && !panels_[j].selected())
00733         {
00734             largest_panel_size = panels_[j].ywidth();
00735             largest_panel = j;
00736         }    
00737     }
00738     
00739     // shrink the line buffer
00740     if(line_buffer_[panels_[i].col()])
00741     {
00742         panels_[i].grow();
00743         --line_buffer_[panels_[i].col()];
00744     }
00745     // shrink the largest panel
00746     else if(largest_panel < panels_.size() && panels_[largest_panel].shrink())
00747     {
00748         panels_[i].grow();
00749     }
00750     
00751 }
00752 
00753 
00754 void goby::util::FlexNCurses::shrink(int i)
00755 {
00756     size_t smallest_panel = panels_.size();
00757     int smallest_panel_size = ymax_;
00758     BOOST_FOREACH(size_t j, unique_panels_)
00759     {
00760         if(panels_[j].ywidth() <= smallest_panel_size
00761            && panels_[j].col() == panels_[i].col()
00762            && !panels_[j].minimized()
00763            && !panels_[j].selected())
00764         {
00765             smallest_panel_size = panels_[j].ywidth();
00766             smallest_panel = j;
00767         }
00768     }
00769     // shrink the panel...
00770     if(panels_[i].shrink())
00771     {
00772         // and add to the the smallest panel
00773         if(smallest_panel < panels_.size())
00774             panels_[smallest_panel].grow();
00775         // or add to the line buffer
00776         else
00777             ++line_buffer_[panels_[i].col()];
00778     }
00779 }
00780     
00781 
00782 void goby::util::FlexNCurses::toggle_minimized(int i)
00783 {
00784     int change = panels_[i].toggle_minimized();
00785     for(int j = 0, m = abs(change); j < m; ++j)
00786         (change/abs(change) == 1) ? grow(i) : shrink(i);
00787 }
00788 
00789 void goby::util::FlexNCurses::winunlock()
00790 {
00791     BOOST_FOREACH(size_t j, unique_panels_)
00792     {        
00793         if(panels_[j].locked())
00794         {
00795             panels_[j].locked(false);
00796             BOOST_FOREACH(size_t k, panels_[j].combined())
00797                 panels_[k].locked(false);
00798     
00799             write_head_title(j);
00800             redraw_lines(j);
00801             lines_from_beg(0,j);
00802         }
00803         
00804     }
00805 
00806     is_locked_ = false;
00807 
00808 }
00809 
00810 void goby::util::FlexNCurses::redraw_lines(int j, int offset /* = -1 */)
00811 {    
00812     wclear(static_cast<WINDOW*>(panels_[j].window()));
00813     
00814     if(offset < 0)
00815     {
00816         const std::multimap<ptime, std::string>& hist = get_history(j, panels_[j].ywidth());
00817         int past = min(static_cast<int>(hist.size()),panels_[j].ywidth()-1) ;
00818 
00819         std::multimap<ptime, std::string>::const_iterator a_it = hist.end();
00820         for(int k = 0; k < past; ++k)
00821             --a_it;
00822         
00823         putlines(j, a_it, hist.end());
00824     }
00825     else
00826     {
00827         const std::multimap<ptime, std::string>& hist = get_history(j);
00828         int past = min(static_cast<int>(hist.size()), panels_[j].ywidth()-1) ;
00829 
00830         int eff_offset = min(static_cast<int>(hist.size())-past, offset);
00831 
00832         std::multimap<ptime, std::string>::const_iterator a_it = hist.begin();
00833         for(int k = 0; k < eff_offset; ++k)
00834             ++a_it;
00835 
00836         std::multimap<ptime, std::string>::const_iterator o_it = a_it;
00837         for(int k = 0; k < past; ++k)
00838             ++o_it;
00839         
00840         putlines(j, a_it, o_it);
00841     }
00842     
00843 }
00844 
00845 
00846 void goby::util::FlexNCurses::winlock()
00847 {
00848     size_t i = panels_.size();
00849     BOOST_FOREACH(size_t j, unique_panels_)
00850     {        
00851         if(panels_[j].selected())
00852         {
00853             i = j;
00854             break;
00855         }    
00856     }
00857     if(i == panels_.size())
00858         return;
00859 
00860     deselect_all();
00861     select(i);
00862     
00863     is_locked_ = true;
00864     locked_panel_ = i;
00865     panels_[i].locked(true);
00866     BOOST_FOREACH(size_t j, panels_[i].combined())
00867         panels_[j].locked(true);
00868 
00869     lines_from_beg(get_history_size(i) - panels_[i].ywidth(), i);
00870     write_head_title(i);
00871 }
00872 
00873 
00874 
00875 void goby::util::FlexNCurses::scroll_up()
00876 {
00877     int i = locked_panel_;
00878     int l = panels_[i].lines_from_beg();    
00879     redraw_lines(i, lines_from_beg(l-1, i));
00880 }
00881 
00882 void goby::util::FlexNCurses::scroll_down()
00883 {
00884     int i = locked_panel_;
00885     int l = panels_[i].lines_from_beg();
00886     redraw_lines(i, lines_from_beg(l+1, i));
00887 }
00888 
00889 void goby::util::FlexNCurses::page_up()
00890 {
00891     int i = locked_panel_;
00892     int l = panels_[i].lines_from_beg();    
00893     redraw_lines(i, lines_from_beg(l-(panels_[i].ywidth()-1), i));
00894 }
00895 
00896 void goby::util::FlexNCurses::page_down()
00897 {
00898     int i = locked_panel_;
00899     int l = panels_[i].lines_from_beg();    
00900     redraw_lines(i, lines_from_beg(l+(panels_[i].ywidth()-1), i));
00901 }
00902 void goby::util::FlexNCurses::scroll_end()
00903 {
00904     int i = locked_panel_;
00905     redraw_lines(i, lines_from_beg(get_history_size(i), i));
00906 }
00907 void goby::util::FlexNCurses::scroll_home()
00908 {
00909     int i = locked_panel_;
00910     redraw_lines(i, lines_from_beg(0, i));
00911 }
00912 
00913 void goby::util::FlexNCurses::restore_order()
00914 {
00915     std::vector<Panel> new_panels;
00916     new_panels.resize(panels_.size());
00917 
00918     BOOST_FOREACH(const Panel& p, panels_)
00919     {
00920         new_panels[p.original_order()] = p;
00921     }
00922     
00923         
00924     panels_ = new_panels;
00925 }
00926 
00927 std::multimap<ptime, std::string> goby::util::FlexNCurses::get_history(size_t i, int how_much /* = -1 */)
00928 {
00929     if(panels_[i].combined().empty())
00930         return panels_[i].history();
00931     else
00932     {
00933         std::multimap<ptime, std::string>::iterator i_it_begin;
00934         if(how_much < 0)
00935             i_it_begin = panels_[i].history().begin();
00936         else
00937         {
00938             i_it_begin = panels_[i].history().end();
00939             for(int k = 0; k < how_much && i_it_begin != panels_[i].history().begin(); ++k)
00940                 --i_it_begin;
00941         }   
00942         
00943         std::multimap<ptime, std::string> merged;
00944         for( ; i_it_begin != panels_[i].history().end(); ++i_it_begin)
00945             merged.insert(*i_it_begin);
00946 
00947         BOOST_FOREACH(size_t j, panels_[i].combined())
00948         {
00949             std::multimap<ptime, std::string>::iterator j_it_begin;
00950             if(how_much < 0)
00951                 j_it_begin = panels_[j].history().begin();
00952             else
00953             {
00954                 j_it_begin = panels_[j].history().end();
00955                 for(int k = 0; k < how_much && j_it_begin != panels_[j].history().begin(); ++k)
00956                     --j_it_begin;
00957             }
00958 
00959             for( ; j_it_begin != panels_[j].history().end(); ++j_it_begin)
00960                 merged.insert(*j_it_begin);
00961         }
00962         return merged;
00963     }
00964 }
00965 
00966             
00967 
00968 
00969 size_t goby::util::FlexNCurses::get_history_size(size_t i)
00970 {
00971     if(panels_[i].combined().empty())
00972         return panels_[i].history().size();
00973     else
00974     {
00975         size_t sum =  panels_[i].history().size();
00976         BOOST_FOREACH(size_t j, panels_[i].combined())
00977         {
00978             sum += panels_[j].history().size();
00979         }
00980         return sum;
00981     }
00982 }
00983 
00984 
00985 
00986 
00987 int goby::util::FlexNCurses::lines_from_beg(int l, size_t i)
00988 {
00989     int hist_size = get_history_size(i);
00990     int past = std::min(hist_size, panels_[i].ywidth());
00991     if(l <= 0)
00992         return panels_[i].lines_from_beg(0);
00993     else if (l >= hist_size-past+1)
00994         return panels_[i].lines_from_beg(hist_size-past+1);
00995     else
00996         return panels_[i].lines_from_beg(l);
00997 }
00998 
00999 
01000 int goby::util::FlexNCurses::Panel::lines_from_beg(int i)
01001 {
01002     return lines_from_beg_ = i;    
01003 }
01004 
01005 int goby::util::FlexNCurses::Panel::minimized(bool b)
01006 {
01007     minimized_ = b;
01008     if(b)
01009     {
01010         unminimized_ywidth_ = ywidth_;
01011         return HEAD_Y - unminimized_ywidth_;
01012     }
01013     else
01014     {
01015         return unminimized_ywidth_ - HEAD_Y;
01016     }
01017 }
01018 
01019 
01020 void goby::util::FlexNCurses::run_input()
01021 {
01022     // sleep(1);
01023     // MOOS loves to stomp on me at startup...
01024     // if(true)
01025     // {
01026     //     boost::mutex::scoped_lock lock(curses_mutex);
01027     //     BOOST_FOREACH(size_t i, unique_panels_)
01028     //     {
01029     //         WINDOW* win = static_cast<WINDOW*>(panels_[i].window());
01030     //         if(win) redrawwin(win);    //         WINDOW* head_win = static_cast<WINDOW*>(panels_[i].head_window());
01031     //         if(head_win) redrawwin(head_win);
01032     //     }
01033     //     BOOST_FOREACH(void* w, vert_windows_)
01034     //     {
01035     //         WINDOW* vert_win = static_cast<WINDOW*>(w);
01036     //         if(vert_win) redrawwin(vert_win);
01037     //     }
01038     //     BOOST_FOREACH(void* w, col_end_windows_)
01039     //     {
01040     //         WINDOW* win = static_cast<WINDOW*>(w);
01041     //         if(win) redrawwin(win);
01042     //     }
01043     //     redrawwin(static_cast<WINDOW*>(foot_window_));
01044     // }
01045     
01046     while(alive_)
01047     {
01048         int k = getch();
01049 
01050         boost::mutex::scoped_lock lock(curses_mutex);
01051         switch(k)
01052         {
01053             // same as resize but restores the order too
01054             case 'r':
01055                 uncombine_all();
01056                 restore_order();
01057             case KEY_RESIZE:
01058                 update_size();
01059                 recalculate_win();
01060                 break;
01061                 
01062             case '1': deselect_all(); select(0); break;
01063             case '2': deselect_all(); select(1); break;
01064             case '3': deselect_all(); select(2); break;
01065             case '4': deselect_all(); select(3); break;
01066             case '5': deselect_all(); select(4); break;
01067             case '6': deselect_all(); select(5); break;
01068             case '7': deselect_all(); select(6); break;
01069             case '8': deselect_all(); select(7); break;
01070             case '9': deselect_all(); select(8); break;
01071             case '0': deselect_all(); select(9); break;
01072 
01073                 // shift + numbers
01074             case '!': select(0); break;
01075             case '@': select(1); break;
01076             case '#': select(2); break;
01077             case '$': select(3); break;
01078             case '%': select(4); break;
01079             case '^': select(5); break;
01080             case '&': select(6); break;
01081             case '*': select(7); break;
01082             case '(': select(8); break;
01083             case ')': select(9); break;
01084 
01085             case 'a': move_left();  break;
01086             case 'd': move_right();    break;                
01087             case 'w': move_up(); break;
01088             case 's': move_down(); break;
01089 
01090             case '+': case '=': grow_all(); break;
01091             case '_': case '-': shrink_all(); break;
01092 
01093             case 'c': combine(); break;
01094             case 'C': uncombine_selected(); break;
01095                 
01096             case 'm':                
01097             case ' ':
01098                 BOOST_FOREACH(size_t i, unique_panels_)
01099             {
01100                 if(panels_[i].selected())
01101                     toggle_minimized(i);
01102             }            
01103             recalculate_win();
01104             break;
01105 
01106             case 'M':                
01107                 BOOST_FOREACH(size_t i, unique_panels_)
01108                 {
01109                     if(!panels_[i].selected())
01110                         toggle_minimized(i);
01111                 }            
01112                 recalculate_win();
01113                 break;
01114 
01115             
01116             case 'D': deselect_all(); break;
01117                 // CTRL-A
01118             case 1: select_all(); break;
01119 
01120             case '\n':
01121             case KEY_ENTER:
01122                 (is_locked_) ? winunlock(): winlock();
01123             break;
01124                 
01125             case KEY_LEFT:
01126                 shift(left());
01127                 break;
01128             case KEY_RIGHT:
01129                 shift(right());
01130                 break;
01131             case KEY_DOWN:
01132                 (!is_locked_) ? shift(down()): scroll_down();
01133                 break;
01134             case KEY_UP:
01135                 (!is_locked_) ? shift(up()): scroll_up();
01136                 break;
01137 
01138             case KEY_PPAGE:
01139                 if(is_locked_) page_up();
01140                 break;
01141                 
01142             case KEY_NPAGE:
01143                 if(is_locked_) page_down();
01144                 break;
01145                     
01146                 
01147             case KEY_END:   (!is_locked_) ? end() : scroll_end();   break;
01148             case KEY_HOME:  (!is_locked_) ? home() : scroll_home();  break;                
01149                 
01150             case KEY_MOUSE:
01151                 MEVENT mort;
01152                 getmouse(&mort);
01153                 size_t gt = find_containing_window(mort.y, mort.x);
01154                 if(gt >= panels_.size())
01155                     break;
01156                 
01157                 switch(mort.bstate)
01158                 {
01159                     case BUTTON1_CLICKED:
01160                     case BUTTON1_PRESSED:
01161                         deselect_all();
01162                         select(gt);
01163                         last_select_x_ = mort.x;
01164                         last_select_y_ = mort.y;
01165                         break;
01166                     case BUTTON1_RELEASED:
01167                         for(int x = min(mort.x, last_select_x_), n = max(mort.x, last_select_x_); x < n; ++x)
01168                         {
01169                             for(size_t y = min(mort.y, last_select_y_), m = max(mort.y, last_select_y_); y < m; ++y)
01170                             {
01171                                 size_t t = find_containing_window(y, x);
01172                                 if(!panels_[t].selected()) select(t);
01173                             }
01174                         }
01175                         
01176                         break;
01177                     case BUTTON1_DOUBLE_CLICKED:
01178                         toggle_minimized(gt);
01179                         recalculate_win();
01180                         break;
01181                 }
01182         }
01183         refresh();
01184     }
01185 }
01186 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends