00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifndef _LISTWIDGET_H
00024 #define _LISTWIDGET_H
00025
00026 #ifdef HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029
00030 #ifdef HAVE_NCURSES_H
00031 # include <ncurses.h>
00032 #else // HAVE_NCURSES_H
00033 # ifdef HAVE_CURSES_H
00034 # include <curses.h>
00035 # else
00036 # error "Neither curses.h nor ncurses.h available"
00037 # endif // HAVE_CURSES_H
00038 #endif // HAVE_NCURSES_H
00039 #include "curswa.h"
00040
00041 #ifdef HAVE_CTYPE_H
00042 # include <ctype.h>
00043 #endif
00044
00045 #ifdef HAVE_FUNCTIONAL
00046 # include <functional>
00047 #endif
00048
00049 #ifdef HAVE_ITERATOR
00050 # include <iterator>
00051 #endif
00052
00053 #ifdef HAVE_LIST
00054 # include <list>
00055 #endif
00056
00057 #ifdef HAVE_ALGORITHM
00058 # include <algorithm>
00059 #endif
00060
00061 #ifdef HAVE_STRING_H
00062 # include <string.h>
00063 #endif
00064
00065 #ifdef HAVE_ASSERT_H
00066 # include <assert.h>
00067 #endif
00068
00069 #include "../intl.h"
00070 #include "uiexception.h"
00071 #include "colors.h"
00072
00073 #include "basewindow.h"
00074
00075 namespace YAPET {
00076 namespace UI {
00077
00092 template<class T>
00093 class ListWidget {
00094 public:
00101 enum SortOrder {
00102 ASCENDING,
00103 DESCENDING
00104 };
00105
00106 private:
00113 class ItemContains : public std::unary_function<T, bool> {
00114 private:
00115 const char* searchterm;
00116 public:
00117 explicit ItemContains (const char* t) :
00118 searchterm (t) {}
00119 bool operator() (const T& item) {
00120 const char* ptr;
00121 #ifdef HAVE_STRCASESTR
00122 ptr = strcasestr (item.c_str(), searchterm);
00123 #elif HAVE_STRSTR
00124 # ifdef HAVE_TOLOWER
00125
00126 size_t haystack_len = strlen (item.c_str() ) + 1;
00127 char* haystack = new char[haystack_len];
00128 strncpy (haystack, item.c_str(), haystack_len);
00129
00130 for (size_t i = 0; i < haystack_len; haystack[i] = (char) tolower (haystack[i]), i++);
00131
00132
00133 size_t needle_len = strlen (searchterm) + 1;
00134 char* needle = new char[needle_len];
00135 strncpy (needle, searchterm, needle_len);
00136
00137 for (size_t i = 0; i < needle_len; needle[i] = (char) tolower (needle[i]), i++);
00138
00139
00140 ptr = strstr (haystack, needle);
00141
00142 memset ( (void*) haystack, 0, haystack_len);
00143 memset ( (void*) needle, 0, needle_len);
00144 delete []haystack;
00145 delete []needle;
00146 # else
00147 ptr = strstr (item.c_str(), searchterm);
00148 # endif // HAVE_TOLOWER
00149 #else
00150 # error "Sorry, neither strcasestr() nor strstr() found"
00151 #endif
00152
00153 if (ptr != NULL)
00154 return true;
00155
00156 return false;
00157 }
00158 };
00159
00160 WINDOW* window;
00161
00162 int width;
00163 int height;
00164
00171 bool hasfocus;
00172
00179 int start_pos;
00180
00189 int cur_pos;
00190
00197 SortOrder sortorder;
00198
00205 typename std::list<T> itemlist;
00206 typedef typename std::list<T>::size_type l_size_type;
00207
00208 typedef typename std::list<T>::iterator list_it;
00209 typedef typename std::list<T>::const_iterator c_list_it;
00210
00217 list_it cur_search_hit;
00218
00224 std::string last_search_term;
00225
00226 inline ListWidget (const ListWidget& lw) {}
00227 inline const ListWidget& operator= (const ListWidget& lw) {
00228 return *this;
00229 }
00230
00244 l_size_type validateIterator (list_it& it) {
00245 l_size_type pos;
00246 list_it itit = itemlist.begin();
00247
00248 for (pos = 0; itit != itemlist.end(); pos++, itit++ )
00249 if (itit == it)
00250 return pos;
00251
00252 return -1;
00253 }
00254
00255 l_size_type validateIterator (c_list_it& it) const {
00256 l_size_type pos;
00257 c_list_it itit = itemlist.begin();
00258
00259 for (pos = 0; itit != itemlist.end(); pos++, itit++ )
00260 if (itit == it)
00261 return pos;
00262
00263 return -1;
00264 }
00265
00266
00267 void highlightItemIter (list_it& it) {
00268 l_size_type pos = validateIterator (it);
00269
00270 if (pos == ( (l_size_type) - 1) ) return;
00271
00272 if ( (pos / pagesize() ) > 0) {
00273 start_pos = pos;
00274 cur_pos = 0;
00275 } else {
00276 start_pos = 0;
00277 cur_pos = pos;
00278 }
00279
00280 showListItems();
00281 showSelected (-1);
00282 }
00283
00292 int setBorder() const {
00293 if (hasfocus)
00294 return box (window, 0, 0);
00295 else
00296 return wborder (window, '|', '|', '-', '-', '+', '+', '+', '+');
00297 }
00298
00299 int pagesize() {
00300 return height - 2;
00301 }
00302
00303 void clearWin() throw (UIException) {
00304 Colors::setcolor (window, LISTWIDGET);
00305 int retval = wclear (window);
00306
00307 if (retval == ERR)
00308 throw UIException (_ ("Error clearing window") );
00309
00310 retval = setBorder();
00311
00312 if (retval == ERR)
00313 throw UIException (_ ("Error drawing box around window") );
00314 }
00315
00316 void showScrollIndicators() throw (UIException) {
00317 if (start_pos > 0) {
00318 int retval = mvwaddch (window,
00319 1,
00320 width - 1,
00321 '^');
00322
00323 if (retval == ERR)
00324 throw UIException (_ ("Unable to display scroll up indicator") );
00325 }
00326
00327 if ( (itemlist.size() - 1) > start_pos + cur_pos &&
00328 itemlist.size() > pagesize() ) {
00329 int retval = mvwaddch (window,
00330 height - 2,
00331 width - 1,
00332 'v');
00333
00334 if (retval == ERR)
00335 throw UIException (_ ("Unable to display scroll down indicator") );
00336 }
00337 }
00338
00339 void showListItems() throw (UIException) {
00340 int usable_width = width - 2;
00341 clearWin();
00342 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00343
00344
00345 for (int i = 0; i < start_pos; itemlist_pos++, i++);
00346
00347 for (int i = 0; i < pagesize() && itemlist_pos != itemlist.end(); itemlist_pos++, i++) {
00348 int retval = mymvwaddnstr (window,
00349 1 + i,
00350 1,
00351 (*itemlist_pos).c_str(),
00352 usable_width);
00353
00354 if (retval == ERR)
00355 throw UIException (_ ("Unable to display item") );
00356 }
00357
00358 showScrollIndicators();
00359 }
00360
00372 void showSelected (int old_pos) throw (UIException) {
00373 int retval = 0;
00374
00375 if (itemlist.size() > 0) {
00376 if (hasfocus)
00377 retval = mymvwchgat (window,
00378 cur_pos + 1,
00379 1,
00380 width - 2,
00381 A_REVERSE,
00382 Colors::getcolor (LISTWIDGET),
00383 NULL);
00384 else
00385 retval = mymvwchgat (window,
00386 cur_pos + 1,
00387 1,
00388 width - 2,
00389 A_NORMAL,
00390 Colors::getcolor (LISTWIDGET),
00391 NULL);
00392
00393 if (retval == ERR)
00394 throw UIException (_ ("Error displaying cursor") );
00395 }
00396
00397 if (old_pos > -1) {
00398
00399 retval = mymvwchgat (window,
00400 old_pos + 1,
00401 1,
00402 width - 2,
00403 A_NORMAL,
00404 Colors::getcolor (LISTWIDGET),
00405 NULL);
00406
00407 if (retval == ERR)
00408 throw UIException (_ ("Error move cursor") );
00409 }
00410
00411 retval = touchwin (window);
00412
00413 if (retval == ERR)
00414 throw UIException (_ ("Error touching window") );
00415
00416 retval = wrefresh (window);
00417
00418 if (retval == ERR)
00419 throw UIException (_ ("Error refreshing window") );
00420 }
00421
00422 void scrollUp() {
00423 if (itemlist.size() == 0) return;
00424
00425 if (cur_pos > 0) {
00426 int old_pos = cur_pos--;
00427 showSelected (old_pos);
00428 } else {
00429 if (start_pos > 0) {
00430 start_pos--;
00431 showListItems();
00432 showSelected (-1);
00433 }
00434 }
00435 }
00436
00437 void scrollDown() {
00438 if (itemlist.size() == 0) return;
00439
00440 if ( ( (l_size_type) (cur_pos + start_pos) ) < itemlist.size() - 1) {
00441 if (cur_pos < pagesize() - 1 ) {
00442 int old_pos = cur_pos++;
00443 showSelected (old_pos);
00444 } else {
00445 if ( ( (l_size_type) start_pos) < itemlist.size() - 1) {
00446 start_pos++;
00447 showListItems();
00448 showSelected (-1);
00449 }
00450 }
00451 }
00452 }
00453
00454 void scrollPageUp() {
00455 if (itemlist.size() == 0) return;
00456
00457 if ( ( (l_size_type) pagesize() ) > itemlist.size() - 1 ) {
00458 cur_pos = 0;
00459 start_pos = 0;
00460 } else {
00461 if ( start_pos - pagesize() > 0 ) {
00462 start_pos -= pagesize();
00463 } else {
00464 start_pos = 0;
00465 }
00466 }
00467
00468 showListItems();
00469 showSelected (-1);
00470 }
00471
00472 void scrollPageDown() {
00473 if (itemlist.size() == 0) return;
00474
00475 if ( ( (l_size_type) pagesize() ) > itemlist.size() - 1 ) {
00476 cur_pos = itemlist.size() - 1;
00477 start_pos = 0;
00478 } else {
00479 if ( itemlist.size() - ( (l_size_type) start_pos + pagesize() ) < pagesize() ) {
00480 start_pos = itemlist.size() - pagesize();
00481 cur_pos = pagesize() - 1;
00482 } else {
00483 start_pos += pagesize();
00484 }
00485
00486 assert (start_pos < itemlist.size() );
00487 assert (start_pos + cur_pos < itemlist.size() );
00488 }
00489
00490 showListItems();
00491 showSelected (-1);
00492 }
00493
00494 void scrollHome() {
00495 if (itemlist.size() == 0) return;
00496
00497 start_pos = 0;
00498 cur_pos = 0;
00499 showListItems();
00500 showSelected (-1);
00501 }
00502
00503 void scrollEnd() {
00504 if (itemlist.size() == 0) return;
00505
00506 start_pos = itemlist.size() - pagesize();
00507
00508 if (start_pos < 0) {
00509 start_pos = 0;
00510 cur_pos = itemlist.size() - 1;
00511 } else {
00512 cur_pos = pagesize() - 1;
00513 }
00514
00515 showListItems();
00516 showSelected (-1);
00517 }
00518
00519 void createWindow (int sx, int sy, int w, int h) throw (UIException) {
00520 window = newwin (h, w, sy, sx);
00521
00522 if (window == NULL)
00523 throw UIException (_ ("Error creating list window") );
00524
00525 Colors::setcolor (window, LISTWIDGET);
00526 int retval = keypad (window, true);
00527
00528 if (retval == ERR)
00529 throw UIException (_ ("Error enabling keypad") );
00530
00531 retval = setBorder();
00532
00533 if (retval == ERR)
00534 throw UIException (_ ("Error re-setting the border") );
00535
00536
00537 width = w;
00538 height = h;
00539 }
00540
00541 public:
00562 ListWidget (std::list<T> l, int sx, int sy, int w, int h)
00563 throw (UIException) : window (NULL),
00564 width (w),
00565 height (h),
00566 hasfocus (false),
00567 start_pos (0),
00568 cur_pos (0),
00569 itemlist (l),
00570 cur_search_hit (itemlist.end() ),
00571 last_search_term ("") {
00572 if ( sx == -1 ||
00573 sy == -1 ||
00574 width == -1 ||
00575 height == -1 )
00576 throw UIException (_ ("No idea of the dimension of the list") );
00577
00578 setSortOrder (ASCENDING);
00579 createWindow (sx, sy, width, height);
00580 }
00581
00582 virtual ~ListWidget() {
00583 wclear (window);
00584 delwin (window);
00585 }
00586
00597 void setList (typename std::list<T>& l) {
00598 itemlist = l;
00599 start_pos = 0;
00600 cur_pos = 0;
00601 setSortOrder (this->sortorder);
00602 refresh();
00603 }
00604
00614 void replaceCurrentItem (T& item) {
00615 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00616
00617 for (int i = 0;
00618 i < (start_pos + cur_pos) && itemlist_pos != itemlist.end();
00619 itemlist_pos++, i++);
00620
00621 *itemlist_pos = item;
00622 setSortOrder (this->sortorder);
00623 }
00624
00625 void deleteSelectedItem() {
00626 if (itemlist.size() == 0) return;
00627
00628 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00629
00630 for (int i = 0;
00631 i < (start_pos + cur_pos) && itemlist_pos != itemlist.end();
00632 itemlist_pos++, i++);
00633
00634 if (itemlist_pos == itemlist.end() ) return;
00635
00636 itemlist.erase (itemlist_pos);
00637 scrollUp();
00638 }
00639
00640
00641 const std::list<T>& getList() const {
00642 return itemlist;
00643 }
00644 std::list<T>& getList() {
00645 return itemlist;
00646 }
00647
00670 virtual int focus() throw (UIException) {
00671 hasfocus = true;
00672 int retval = setBorder();
00673
00674 if (retval == ERR)
00675 throw UIException (_ ("Error setting the border of window") );
00676
00677 showScrollIndicators();
00678 showSelected (-1);
00679 retval = wrefresh (window);
00680
00681 if (retval == ERR)
00682 throw UIException (_ ("Error refreshing the list widget") );
00683
00684 int ch = 0;
00685
00686 while (hasfocus) {
00687 ch = wgetch (window);
00688
00689 switch (ch) {
00690 case KEY_UP:
00691 case 'k':
00692 scrollUp();
00693 break;
00694 case KEY_DOWN:
00695 case 'j':
00696 scrollDown();
00697 break;
00698 case KEY_HOME:
00699 case KEY_A1:
00700 scrollHome();
00701 break;
00702 case KEY_END:
00703 case KEY_C1:
00704 scrollEnd();
00705 break;
00706 case KEY_NPAGE:
00707 case KEY_C3:
00708 scrollPageDown();
00709 break;
00710 case KEY_PPAGE:
00711 case KEY_A3:
00712 scrollPageUp();
00713 break;
00714 case KEY_REFRESH:
00715 BaseWindow::refreshAll();
00716 break;
00717 default:
00718 hasfocus = false;
00719 break;
00720 }
00721 }
00722
00723 showSelected (-1);
00724 retval = setBorder();
00725
00726 if (retval == ERR)
00727 throw UIException (_ ("Error re-setting the border") );
00728
00729 retval = wrefresh (window);
00730
00731 if (retval == ERR)
00732 throw UIException (_ ("Error refreshing the list widget") );
00733
00734 return ch;
00735 }
00736
00737 void refresh() throw (UIException) {
00738 showListItems();
00739 showSelected (-1);
00740 int retval = wrefresh (window);
00741
00742 if (retval == ERR)
00743 throw UIException (_ ("Error refreshing list") );
00744 }
00745
00746 void resize (int sx, int sy, int w, int h) throw (UIException) {
00747 int retval = wclear (window);
00748
00749 if (retval == ERR)
00750 throw UIException (_ ("Error clearing list") );
00751
00752 retval = wrefresh (window);
00753
00754 if (retval == ERR)
00755 throw UIException (_ ("Error refreshing window") );
00756
00757 retval = delwin (window);
00758
00759 if (retval == ERR)
00760 throw UIException (_ ("Error deleting window") );
00761
00762 createWindow (sx, sy, w, h);
00763 }
00764
00765 int getListPos() {
00766 return start_pos + cur_pos;
00767 }
00768
00769 T getSelectedItem() {
00770 typename std::list<T>::iterator itemlist_pos = itemlist.begin();
00771
00772 for (int i = 0;
00773 i < (start_pos + cur_pos) && itemlist_pos != itemlist.end();
00774 itemlist_pos++, i++);
00775
00776 return *itemlist_pos;
00777 }
00778
00779 l_size_type size() {
00780 return itemlist.size();
00781 }
00782
00790 SortOrder getSortOrder() const {
00791 return sortorder;
00792 }
00793
00802 void setSortOrder (SortOrder so) {
00803 itemlist.sort();
00804 sortorder = so;
00805
00806 switch (sortorder) {
00807 case ASCENDING:
00808 break;
00809 case DESCENDING:
00810 std::reverse (itemlist.begin(), itemlist.end() );
00811 break;
00812 }
00813 }
00814
00822 void setSortOrder() {
00823 setSortOrder (getSortOrder() );
00824 }
00825
00835 bool searchTerm (const char* t) {
00836 last_search_term = t;
00837 cur_search_hit = std::find_if (itemlist.begin(),
00838 itemlist.end(),
00839 ItemContains (t) );
00840
00841 if (cur_search_hit != itemlist.end() ) {
00842 highlightItemIter (cur_search_hit);
00843 return true;
00844 }
00845
00846 return false;
00847 }
00848
00856 bool searchNext() {
00857 if (validateIterator (cur_search_hit) == ( (l_size_type) - 1) ||
00858 last_search_term.empty() )
00859 return false;
00860
00861
00862
00863 if (cur_search_hit == itemlist.end() ) {
00864 cur_search_hit = itemlist.begin();
00865 } else {
00866
00867
00868 cur_search_hit++;
00869 }
00870
00871
00872
00873
00874
00875 if (cur_search_hit == itemlist.end() ) return false;
00876
00877 cur_search_hit = std::find_if (cur_search_hit,
00878 itemlist.end(),
00879 ItemContains (last_search_term.c_str() ) );
00880
00881 if (cur_search_hit != itemlist.end() ) {
00882 highlightItemIter (cur_search_hit);
00883 return true;
00884 }
00885
00886 return false;
00887 }
00888
00889 };
00890
00891 }
00892 }
00893 #endif // _LISTWIDGET_H