Goby Underwater Autonomy Project
Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
|
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