00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifndef _LISTWIDGET_H
00023 #define _LISTWIDGET_H
00024
00025 #ifdef HAVE_CONFIG_H
00026 # include <config.h>
00027 #endif
00028
00029 #ifdef HAVE_NCURSES_H
00030 # include <ncurses.h>
00031 #else // HAVE_NCURSES_H
00032 # ifdef HAVE_CURSES_H
00033 # include <curses.h>
00034 # else
00035 # error "Neither curses.h nor ncurses.h available"
00036 # endif // HAVE_CURSES_H
00037 #endif // HAVE_NCURSES_H
00038 #include "curswa.h"
00039
00040 #ifdef HAVE_FUNCTIONAL
00041 # include <functional>
00042 #endif
00043
00044 #ifdef HAVE_ITERATOR
00045 # include <iterator>
00046 #endif
00047
00048 #ifdef HAVE_LIST
00049 # include <list>
00050 #endif
00051
00052 #ifdef HAVE_STRING_H
00053 # include <string.h>
00054 #endif
00055
00056 #include "uiexception.h"
00057 #include "colors.h"
00058 #include "basewindow.h"
00059
00060 namespace YAPETUI {
00061
00076 template<class T>
00077 class ListWidget {
00078 public:
00085 enum SortOrder {
00086 ASCENDING,
00087 DESCENDING
00088 };
00089
00090 private:
00097 class ItemContains : public std::unary_function<T,bool> {
00098 private:
00099 const char* searchterm;
00100 public:
00101 explicit ItemContains(const char* t) :
00102 searchterm(t) {}
00103 bool operator()(const T& item) {
00104 const char* ptr;
00105 #ifdef HAVE_STRCASESTR
00106 ptr = strcasestr(item.c_str(), searchterm);
00107 #elif HAVE_STRSTR
00108 ptr = strstr(item.c_str(), searchterm);
00109 #else
00110 # error "Sorry, neither strcasestr() nor strstr() found"
00111 #endif
00112 if (ptr != NULL)
00113 return true;
00114
00115 return false;
00116 }
00117 };
00118
00119 WINDOW* window;
00120
00121 int width;
00122 int height;
00123
00130 bool hasfocus;
00131
00138 int start_pos;
00139
00148 int cur_pos;
00149
00156 SortOrder sortorder;
00157
00164 typename std::list<T> itemlist;
00165 typedef typename std::list<T>::size_type l_size_type;
00166
00167 typedef typename std::list<T>::iterator list_it;
00168 typedef typename std::list<T>::const_iterator c_list_it;
00169
00176 list_it cur_search_hit;
00177
00183 std::string last_search_term;
00184
00185 inline ListWidget(const ListWidget& lw) {}
00186 inline const ListWidget& operator=(const ListWidget& lw) {
00187 return *this; }
00188
00202 l_size_type validateIterator(list_it& it) {
00203 l_size_type pos;
00204 list_it itit = itemlist.begin();
00205
00206 for (pos = 0; itit != itemlist.end(); pos++, itit++ )
00207 if (itit == it)
00208 return pos;
00209
00210 return -1;
00211 }
00212
00213 l_size_type validateIterator(c_list_it& it) const {
00214 l_size_type pos;
00215 c_list_it itit = itemlist.begin();
00216
00217 for (pos = 0; itit != itemlist.end(); pos++, itit++ )
00218 if (itit == it)
00219 return pos;
00220
00221 return -1;
00222 }
00223
00224
00225 void highlightItemIter(list_it& it) {
00226 l_size_type pos = validateIterator(it);
00227
00228 if (pos < 0) return;
00229
00230 if ( (pos/pagesize()) > 0) {
00231 start_pos=pos;
00232 cur_pos=0;
00233 } else {
00234 start_pos = 0;
00235 cur_pos = pos;
00236 }
00237
00238 showListItems();
00239 showSelected(-1);
00240 }
00241
00250 int setBorder() const {
00251 if (hasfocus)
00252 return box(window,0,0);
00253 else
00254 return wborder(window, '|', '|', '-', '-', '+', '+', '+', '+');
00255 }
00256
00257 int pagesize() { return height-2; }
00258
00259 void clearWin() throw(UIException) {
00260 Colors::setcolor(window, LISTWIDGET);
00261 int retval = wclear(window);
00262 if (retval == ERR)
00263 throw UIException("Error clearing window");
00264
00265 retval = setBorder();
00266 if (retval == ERR)
00267 throw UIException("Error drawing box around window");
00268 }
00269
00270 void showScrollIndicators() throw(UIException) {
00271 if (start_pos > 0) {
00272 int retval = mvwaddch(window,
00273 1,
00274 width - 1,
00275 '^');
00276 if (retval == ERR)
00277 throw UIException("Unable to display scroll up indicator");
00278 }
00279
00280 if ( (itemlist.size() - 1) > start_pos + cur_pos &&
00281 itemlist.size() > pagesize()) {
00282 int retval = mvwaddch(window,
00283 height - 2,
00284 width - 1,
00285 'v');
00286 if (retval == ERR)
00287 throw UIException("Unable to display scroll down indicator");
00288 }
00289 }
00290
00291 void showListItems() throw(UIException) {
00292 int usable_width = width - 2;
00293
00294 clearWin();
00295
00296 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00297
00298 for(int i=0; i<start_pos; itemlist_pos++,i++);
00299
00300 for(int i=0; i<pagesize() && itemlist_pos != itemlist.end(); itemlist_pos++, i++) {
00301 int retval = mymvwaddnstr(window,
00302 1 + i,
00303 1,
00304 (*itemlist_pos).c_str(),
00305 usable_width);
00306 if (retval == ERR)
00307 throw UIException("Unable to display item");
00308 }
00309
00310 showScrollIndicators();
00311 }
00312
00324 void showSelected(int old_pos) throw(UIException) {
00325 int retval = 0;
00326
00327 if (itemlist.size() > 0) {
00328 if (hasfocus)
00329 retval = mymvwchgat(window,
00330 cur_pos + 1,
00331 1,
00332 width-2,
00333 A_REVERSE,
00334 Colors::getcolor(LISTWIDGET),
00335 NULL);
00336 else
00337 retval = mymvwchgat(window,
00338 cur_pos + 1,
00339 1,
00340 width-2,
00341 A_NORMAL,
00342 Colors::getcolor(LISTWIDGET),
00343 NULL);
00344 if (retval == ERR)
00345 throw UIException("Error displaying cursor");
00346
00347 }
00348
00349 if (old_pos > -1) {
00350
00351 retval = mymvwchgat(window,
00352 old_pos + 1,
00353 1,
00354 width-2,
00355 A_NORMAL,
00356 Colors::getcolor(LISTWIDGET),
00357 NULL);
00358 if (retval == ERR)
00359 throw UIException("Error move cursor");
00360 }
00361
00362 retval = touchwin(window);
00363 if (retval == ERR)
00364 throw UIException("Error touching window");
00365
00366 retval = wrefresh(window);
00367 if (retval == ERR)
00368 throw UIException("Error refreshing window");
00369 }
00370
00371 void scrollUp() {
00372 if (itemlist.size() == 0) return;
00373
00374 if (cur_pos>0) {
00375 int old_pos = cur_pos--;
00376 showSelected(old_pos);
00377 } else {
00378 if (start_pos>0) {
00379 start_pos--;
00380 showListItems();
00381 showSelected(-1);
00382 }
00383 }
00384 }
00385
00386 void scrollDown() {
00387 if (itemlist.size() == 0) return;
00388
00389 if ( ((l_size_type)(cur_pos+start_pos))<itemlist.size()-1) {
00390 if (cur_pos < pagesize()-1 ) {
00391 int old_pos = cur_pos++;
00392 showSelected(old_pos);
00393 } else {
00394 if (((l_size_type)start_pos)<itemlist.size()-1) {
00395 start_pos++;
00396 showListItems();
00397 showSelected(-1);
00398 }
00399 }
00400 }
00401 }
00402
00403 void scrollPageUp() {
00404 if (itemlist.size() == 0) return;
00405
00406 int old_pos = cur_pos;
00407 cur_pos = 0;
00408 if ( start_pos - pagesize() > 0 ) {
00409 start_pos -= pagesize();
00410 }else {
00411 start_pos = 0;
00412 }
00413 showListItems();
00414 showSelected(old_pos);
00415 }
00416
00417 void scrollPageDown() {
00418 if (itemlist.size() == 0) return;
00419
00420 int old_pos = cur_pos;
00421 if ( ((l_size_type)pagesize()) > itemlist.size() - 1 ) {
00422 cur_pos = itemlist.size() - 1;
00423 start_pos = 0;
00424 } else {
00425 start_pos += pagesize() - 1;
00426 if ( ((l_size_type)start_pos) > itemlist.size() -1 ) {
00427 start_pos = itemlist.size() - pagesize() - 1;
00428 }
00429 cur_pos = 0;
00430 }
00431
00432 showListItems();
00433 showSelected(old_pos);
00434 }
00435
00436 void scrollHome() {
00437 if (itemlist.size() == 0) return;
00438
00439 start_pos = 0;
00440 int old_pos = cur_pos;
00441 cur_pos = 0;
00442 showListItems();
00443 showSelected(old_pos);
00444 }
00445
00446 void scrollEnd() {
00447 if (itemlist.size() == 0) return;
00448
00449 int old_pos = cur_pos;
00450 start_pos = itemlist.size() - pagesize();
00451 if (start_pos < 0) {
00452 start_pos = 0;
00453 cur_pos = itemlist.size()-1;
00454 } else {
00455 cur_pos = pagesize()-1;
00456 }
00457
00458 showListItems();
00459 showSelected(old_pos);
00460 }
00461
00462 void createWindow(int sx, int sy, int w, int h) throw(UIException) {
00463 window = newwin(h, w, sy, sx);
00464 if (window == NULL)
00465 throw UIException("Error creating list window");
00466
00467 Colors::setcolor(window, LISTWIDGET);
00468
00469 int retval = keypad(window, true);
00470 if (retval == ERR)
00471 throw UIException("Error enabling keypad");
00472
00473 retval = setBorder();
00474 if (retval == ERR)
00475 throw UIException("Error re-setting the border");
00476
00477
00478 width = w;
00479 height = h;
00480 }
00481
00482 public:
00503 ListWidget(std::list<T> l, int sx, int sy, int w, int h)
00504 throw(UIException) : window(NULL),
00505 width(w),
00506 height(h),
00507 hasfocus(false),
00508 start_pos(0),
00509 cur_pos(0),
00510 itemlist(l),
00511 cur_search_hit(itemlist.end()),
00512 last_search_term("") {
00513 if ( sx == -1 ||
00514 sy == -1 ||
00515 width == -1 ||
00516 height == -1 )
00517 throw UIException("No idea of the dimension of the list");
00518
00519 setSortOrder(ASCENDING);
00520
00521 createWindow(sx, sy, width, height);
00522 }
00523
00524 virtual ~ListWidget() {
00525 wclear(window);
00526 delwin(window);
00527 }
00528
00539 void setList(typename std::list<T>& l) {
00540 itemlist = l;
00541 start_pos = 0;
00542 cur_pos = 0;
00543 setSortOrder(this->sortorder);
00544 refresh();
00545 }
00546
00556 void replaceCurrentItem(T& item) {
00557 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00558 for (int i=0;
00559 i<(start_pos + cur_pos) && itemlist_pos != itemlist.end();
00560 itemlist_pos++, i++);
00561
00562 *itemlist_pos = item;
00563 setSortOrder(this->sortorder);
00564 }
00565
00566 void deleteSelectedItem() {
00567 if (itemlist.size()==0) return;
00568 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00569 for (int i=0;
00570 i<(start_pos + cur_pos) && itemlist_pos != itemlist.end();
00571 itemlist_pos++, i++);
00572
00573 if (itemlist_pos == itemlist.end()) return;
00574 itemlist.erase(itemlist_pos);
00575 scrollUp();
00576 }
00577
00578
00579 const std::list<T>& getList() const { return itemlist; }
00580 std::list<T>& getList() { return itemlist; }
00581
00604 virtual int focus() throw(UIException) {
00605 hasfocus = true;
00606
00607 int retval = setBorder();
00608 if (retval == ERR)
00609 throw UIException("Error setting the border of window");
00610
00611 showScrollIndicators();
00612 showSelected(-1);
00613
00614 retval = wrefresh(window);
00615 if (retval == ERR)
00616 throw UIException("Error refreshing the list widget");
00617
00618 int ch;
00619 while (hasfocus) {
00620 ch = wgetch(window);
00621 switch (ch) {
00622 case KEY_UP:
00623 scrollUp();
00624 break;
00625 case KEY_DOWN:
00626 scrollDown();
00627 break;
00628 case KEY_HOME:
00629 case KEY_A1:
00630 scrollHome();
00631 break;
00632 case KEY_END:
00633 case KEY_C1:
00634 scrollEnd();
00635 break;
00636 case KEY_NPAGE:
00637 case KEY_C3:
00638 scrollPageDown();
00639 break;
00640 case KEY_PPAGE:
00641 case KEY_A3:
00642 scrollPageUp();
00643 break;
00644 case KEY_REFRESH:
00645 BaseWindow::refreshAll();
00646 break;
00647 default:
00648 hasfocus = false;
00649 break;
00650 }
00651 }
00652
00653 showSelected(-1);
00654
00655 retval = setBorder();
00656 if (retval == ERR)
00657 throw UIException("Error re-setting the border");
00658 retval = wrefresh(window);
00659 if (retval == ERR)
00660 throw UIException("Error refreshing the list widget");
00661
00662 return ch;
00663 }
00664
00665 void refresh() throw(UIException) {
00666 showListItems();
00667 showSelected(-1);
00668
00669 int retval = wrefresh(window);
00670 if (retval == ERR)
00671 throw UIException("Error refreshing list");
00672 }
00673
00674 void resize(int sx, int sy, int w, int h) throw(UIException) {
00675 int retval = wclear(window);
00676 if (retval == ERR)
00677 throw UIException("Error clearing list");
00678
00679 retval = wrefresh(window);
00680 if (retval == ERR)
00681 throw UIException("Error refreshing window");
00682
00683 retval = delwin(window);
00684 if (retval == ERR)
00685 throw UIException("Error deleting window");
00686
00687 createWindow(sx, sy, w, h);
00688 }
00689
00690 int getListPos() { return start_pos + cur_pos; }
00691
00692 T getSelectedItem() {
00693 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00694 for (int i=0;
00695 i<(start_pos + cur_pos) && itemlist_pos != itemlist.end();
00696 itemlist_pos++, i++);
00697 return *itemlist_pos;
00698 }
00699
00700 l_size_type size() { return itemlist.size(); }
00701
00709 SortOrder getSortOrder() const { return sortorder; }
00710
00719 void setSortOrder(SortOrder so) {
00720 itemlist.sort();
00721 sortorder = so;
00722 switch (sortorder) {
00723 case ASCENDING:
00724 break;
00725 case DESCENDING:
00726 std::reverse(itemlist.begin(),itemlist.end());
00727 break;
00728 }
00729 }
00730
00738 void setSortOrder() {
00739 setSortOrder(getSortOrder());
00740 }
00741
00751 bool searchTerm(const char* t) {
00752 last_search_term = t;
00753 cur_search_hit = std::find_if(itemlist.begin(),
00754 itemlist.end(),
00755 ItemContains(t));
00756
00757 if (cur_search_hit != itemlist.end()) {
00758 highlightItemIter(cur_search_hit);
00759 return true;
00760 }
00761
00762 return false;
00763 }
00764
00772 bool searchNext() {
00773 if (validateIterator(cur_search_hit) < 0 ||
00774 last_search_term.empty() )
00775 return false;
00776
00777
00778
00779 if (cur_search_hit == itemlist.end() ) {
00780 cur_search_hit = itemlist.begin();
00781 } else {
00782
00783
00784 cur_search_hit++;
00785 }
00786
00787
00788
00789
00790
00791 if (cur_search_hit == itemlist.end()) return false;
00792
00793 cur_search_hit = std::find_if(cur_search_hit,
00794 itemlist.end(),
00795 ItemContains(last_search_term.c_str()));
00796
00797 if (cur_search_hit != itemlist.end()) {
00798 highlightItemIter(cur_search_hit);
00799 return true;
00800 }
00801
00802 return false;
00803
00804 }
00805
00806 };
00807
00808 }
00809 #endif // _LISTWIDGET_H