• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

ui/listwidget.h

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 //
00003 // $Id: listwidget.h 3342 2010-09-17 18:32:00Z java $
00004 //
00005 // Copyright (C) 2008-2010  Rafael Ostertag
00006 //
00007 // This file is part of YAPET.
00008 //
00009 // YAPET is free software: you can redistribute it and/or modify it under the
00010 // terms of the GNU General Public License as published by the Free Software
00011 // Foundation, either version 3 of the License, or (at your option) any later
00012 // version.
00013 //
00014 // YAPET is distributed in the hope that it will be useful, but WITHOUT ANY
00015 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00016 // FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
00017 // details.
00018 //
00019 // You should have received a copy of the GNU General Public License along with
00020 // YAPET.  If not, see <http://www.gnu.org/licenses/>.
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" // Leave this here. It depends on the above includes.
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 // BaseWindow::refreshAll()
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                             // Prepare the haystack
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                             // Prepare needle
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                             // Now do the search
00140                             ptr = strstr (haystack, needle);
00141                             // Destroy that properly
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                     // Advance to the start point
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                         // Reset the old position to 'normal' attribute
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                     // We set them here in case the window was resized
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': // vi key
00692                                 scrollUp();
00693                                 break;
00694                             case KEY_DOWN:
00695                             case 'j': // vi key
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                     // Here make the search start at the beginning, or
00862                     // continue at the next item
00863                     if (cur_search_hit == itemlist.end() ) {
00864                         cur_search_hit = itemlist.begin();
00865                     } else {
00866                         // Advance to the next item. Else we would have a
00867                         // hit on the item pointed to by cur_search_hit...
00868                         cur_search_hit++;
00869                     }
00870 
00871                     // Indicate that we reached the end of the list. Upon
00872                     // the next call of this method, cur_search_hit will
00873                     // be set to the beginning of the list and the search
00874                     // starts from the top again
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

Generated on Sun Sep 19 2010 15:37:13 for YAPET by  doxygen 1.7.1