tableviewer.cxx

Go to the documentation of this file.
00001 //
00002 // Copyright 2010, Lowell Boggs Jr.
00003 //
00004 // This file or directory, containing source code for a computer program,
00005 // is Copyrighted by Lowell Boggs, Jr.  987 Regency Drive, Lewisville
00006 // TX (USA), 75067.  You may use, copy, modify, and distribute this
00007 // source file without charge or obligation so long as you agree to
00008 // the following:
00009 //
00010 //  1.  You must indemnify Lowell Boggs against any and all financial
00011 //      obligations caused by its use, misuse, function, or malfunction.
00012 //      Further, you acknowledge that there is no warranty of any kind,
00013 //      whatsoever.
00014 //
00015 //  2.  You agree not to attempt to patent any portion of this original
00016 //      work -- though you may attempt to patent your own extensions to
00017 //      it if you so choose.
00018 //
00019 //  3.  You keep this copyright notice with the file and all copies
00020 //      of the file and do not change it anyway except language translation.
00021 //
00022 // You are responsible for enforcing your own compliance with these
00023 // conditions and may not use this source file if you cannot agree to the
00024 // above terms and conditions.
00025 //
00026 // Warning:  not all files in this directory structure are covered by the
00027 // same copyright.  Some of them are part of the GNU source distribution
00028 // and you must obey the GPL copyright for those files.
00029 
00030 #include <cxxtls/tableviewer.h>
00031 #include <cxxtls/viewermanager.h>
00032 #include <algorithm>
00033 #include <cxxtls/simple_regex.h>
00034 
00035 #include <ctype.h>
00036 
00037 using namespace std;
00038 
00039 namespace cxxtls
00040 {
00041 
00044 TableViewer::~TableViewer()
00045 {
00046 }
00047 
00048 void TableViewer::repaintTitle( CursorWindow::viewport *vp )
00049 {
00050   vp->set_curpos(0,0);
00051   vp->set_text_attribute(active_ ?  manager_->active_title_att
00052                                  :  manager_->inactive_title_att
00053                         );
00054                         
00055   *vp << application_name();
00056 
00057   vp->fill_to_eol();
00058 
00059 }
00060 
00063 void TableViewer::operator() ( CursorWindow::viewport *     vp,
00064                                int                          cmd
00065                              )
00066 {
00067   // WARNING:  do not save vp -- it gets deleted regularly!
00068   // repaint the current visible page
00069 
00071   // Determine if the page is active or inactive based on the
00072   // cmd variable passed to us.
00074 
00075   hideCursor(vp);
00076 
00077 
00078   if(cmd == CursorWindow::viewport::repaint_handler::activate)
00079     active_ = true;
00080   else
00081   if(cmd == CursorWindow::viewport::repaint_handler::deactivate)
00082   {
00083     active_ = false;
00084   }
00085 
00087   // Repaint the title line in text attributes just determined
00089 
00090 
00091   repaintTitle(vp);
00092 
00093 
00095   // Set up to recompute the viewport dimensions and the needs of
00096   // the table.
00098 
00099 
00100   pageWidth_  = 0;
00101   pageHeight_ = 0;
00102 
00103   recomputeLayout(); // if dirty...
00104 
00105   row_col size    = vp->size();
00106 
00107 
00108   int row;
00109 
00110   vp->set_text_attribute(manager_->normal_att);
00111 
00112 
00113   displayOrigin_.row_ = 2; // 1 for app name and 1 for column headers
00114   displayOrigin_.col_ = longestRowTitle();
00115 
00116 
00117    pageWidth_ = 0;
00118 
00119    size_t gutterWidth = 1;
00120 
00121 
00123   //  Handle the display of column headers  (and also calculate
00124   //  the widthds of the columns in the actual table).
00126 
00127    // first, find out how wide the columns are and paint the titles
00128    {
00129        size_t col;
00130 
00131        colWidths_.resize(0);
00132 
00133        for(col=0; col < cols_.size(); ++col)
00134        {
00135           // handle all worldColumns
00136 
00137           size_t colWidth = columnWidth(col);
00138     
00139           colWidths_.push_back(colWidth);
00140        }
00141 
00142        size_t paintColumn = displayOrigin_.col_;
00143 
00144        vp->set_text_attribute(CursorWindow::bottom_att);
00145        vp->set_curpos(displayOrigin_.row_-1,0);
00146        vp->fill_to_eol();
00147 
00148        vp->set_curpos(displayOrigin_.row_-1, paintColumn);
00149 
00150 
00151        for(col=0; col < cols_.size() - originCol(); ++col)
00152        {
00153            size_t workingCol = originCol() + col;
00154 
00155            size_t colWidth = colWidths_[workingCol];
00156 
00157 
00158            vp->set_curpos(displayOrigin_.row_-1, paintColumn + gutterWidth);
00159 
00160 
00161            *vp << cols_[workingCol].title_;
00162 
00163            if(paintColumn + colWidth + gutterWidth > size_t(size.col_))
00164            {
00165              // once a column has gone past the end of the current page
00166              // break out of this loop.  Keep the offending column and let
00167              // clipping handle the display issues, but keep this column
00168              // otherwise we'll never see any part of a big column...
00169 
00170              ++col; // col becomes page width below, so
00171                     // we need it to be 1 more than the current
00172                     // column
00173 
00174              break;
00175            }
00176 
00177            paintColumn +=  colWidth + gutterWidth;
00178 
00179        }
00180 
00181        pageWidth_ = col;  // saved in TableViewer class for use by paintCursor
00182 
00183        vp->fill_to_eol();
00184 
00185    }
00186    
00187 
00189   // Now paint the data in the rows.
00191 
00192   vp->set_text_attribute(manager_->normal_att);
00193 
00194 
00195   size_t lastPaintedRow=1000;
00196 
00197   // in the following algorithm, "row" refers to the virtual layout_ world, not the screen.
00198 
00199   pageHeight_ = 0;
00200 
00201   for(row=0; row < (size.row_ - displayOrigin_.row_); ++row)
00202   {
00203       size_t workingRow = originRow() + row;
00204 
00205       if(workingRow >= layout_.size() || workingRow >= rows() )
00206           break;
00207 
00208       ++pageHeight_;  // in layout_ rows
00209 
00210       LayoutType::value_type &curRow = layout_[workingRow];
00211 
00212       vp->set_curpos(row+displayOrigin_.row_, 0);
00213 
00214       vp->set_text_attribute(manager_->normal_att);
00215 
00216       *vp << rows_[workingRow].title_;
00217 
00218       {
00219          row_col curpos = vp->curpos();
00220 
00221          int colDelta = displayOrigin_.col_ - curpos.col_;
00222 
00223          for(int i = 0; i < colDelta; ++i)
00224          {
00225            *vp << " ";
00226          }
00227 
00228       }
00229 
00230       if(curRow.size())
00231       {
00232          for(size_t col = 0; col < pageWidth_; ++col)
00233          {
00234              size_t workingCol = originCol() + col;
00235 
00236              int worldCol = curRow[workingCol].worldColumn_ -
00237                             curRow[originCol()].worldColumn_;
00238     
00239              if(worldCol < size.col_)
00240              {
00241                  paintCell(vp,
00242                            lastPaintedRow = row+displayOrigin_.row_,
00243                            worldCol+displayOrigin_.col_,
00244                            workingRow,
00245                            workingCol,
00246                            false,  // not highlighed
00247                            false   // don't position cursor to edit colum
00248                           );
00249     
00250              }
00251          }
00252       }
00253 
00254       vp->set_text_attribute(manager_->normal_att);
00255 
00256       vp->fill_to_eol();
00257 
00258   }
00259 
00261   // clear out any text appearing after the last row.
00263 
00264   vp->set_text_attribute(manager_->normal_att);
00265 
00266   while(lastPaintedRow < size_t(size.row_))
00267   {
00268     ++lastPaintedRow;
00269     vp->set_curpos(lastPaintedRow, 0);
00270     vp->fill_to_eol();
00271   }
00272 
00273  //
00274  //  ADJUST CURSOR POSITION TO FIT IN THE PAINTED REGION
00275  //
00276 
00277  //  setCursorInfo(vp, originRow(), originCol(), active_);
00278 
00279   cursorDisplayed_ = active_;
00280 
00281   if(cursorRow_ > rows())
00282     cursorRow_ = originRow();
00283 
00284   if(cursorCol_ > cols())
00285     cursorCol_ = originCol();
00286 
00287   if(pageHeight_ && (cursorRow_ >= originRow() + pageHeight_) )
00288   {
00289       size_t lastRow = originRow() + (pageHeight_ -1);
00290 
00291       if(lastRow > 1)
00292       {
00293          cursorRow_ = lastRow-1;
00294       }
00295       else
00296       {
00297          cursorRow_ = 0;
00298       }
00299 
00300 
00301   }
00302 
00303   if(pageWidth_ && (cursorCol_ >= originCol() + pageWidth_) )
00304   {
00305       // note that it is possible for the last column to be
00306       // only partially displayed.
00307 
00308       size_t lastCol = originCol() + (pageWidth_ -1);
00309 
00310       if(lastCol > 1)
00311       {
00312          cursorCol_ = lastCol-1;
00313       }
00314       else
00315       {
00316          cursorCol_ = 0;
00317       }
00318 
00319 
00320   }
00321 
00322   if(cursorCol_ < originCol())
00323      cursorCol_ = originCol();
00324 
00325   if(cursorRow_ < originRow())
00326      cursorRow_ = originRow();
00327 
00328   if(cursorDisplayed_)
00329     showCursor(vp);
00330 
00331 
00332 }
00333 
00336 
00337 bool TableViewer::handle_event( CursorWindow::input_event const * e,
00338                                 CursorWindow::viewport          * vp
00339                               )
00340 {
00341   vp = vp;
00342   
00343   if(e)
00344   {
00345       if(    e->type_ == CursorWindow::input_event::DataKey
00346          ||  e->type_ == CursorWindow::input_event::FunctionKey 
00347         )
00348       {
00349          static RowInfo bi; // blank info
00350 
00351          switch(e->value_)
00352          {
00353              case 'q': return true;
00354              case 'Q': return true;
00355 
00356              case CursorWindow::key_up:     moveUp(vp);            return false;
00357              case CursorWindow::key_down:   moveDown(vp);          return false;
00358              case CursorWindow::key_left:   moveLeft(vp);          return false;
00359              case CursorWindow::key_right:  moveRight(vp);         return false;
00360              case 0x09:                     pageRight(vp);         return false; // tab
00361              case CursorWindow::key_btab:   pageLeft(vp);          return false;
00362              case CursorWindow::key_home:   pageHome(vp);          return false;
00363              case CursorWindow::key_next:   pageDown(vp);          return false;
00364              case CursorWindow::key_prior:  pageUp(vp);            return false;
00365              case CursorWindow::key_end:    pageEnd(vp);           return false;
00366              case 0x02:                     pageBottom(vp);        return false; // ^B
00367              case 0x14:                     pageTop(vp);           return false; // ^T
00368              case 0x13:                     findFirst(vp);         return false; // ^S
00369              case 0x0e:                     findNext(vp,false);    return false; // ^N
00370 
00372              case CursorWindow::key_f9:     
00373              {
00374 
00375                  CursorInfo tmp = cursorInfo();
00376 
00377                  insertRowAbove(vp,bi); 
00378 
00379 
00380                  (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00381                  setCursorInfo(vp, tmp);
00382              }
00383              return false;
00385              case CursorWindow::key_f11:    
00386              {
00387                  CursorInfo tmp = cursorInfo();
00388 
00389                  insertRowBelow(vp,bi); 
00390                  (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00391                  setCursorInfo(vp, tmp);
00392              }
00393              return false;
00395              case CursorWindow::key_f8:    
00396              {
00397                  CursorInfo tmp = cursorInfo();
00398 
00399                  deleteRow(vp); 
00400                  (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00401                  setCursorInfo(vp, tmp);
00402              }
00403              return false;
00404 
00405 
00406          }
00407       }
00408   }
00409 
00410   return true;  // any key terminates input at this time
00411 }
00412 
00415 
00416 string const &TableViewer::application_name() const
00417 {
00418      static string empty("some table");
00419 
00420      return empty;
00421 }
00424 
00425 string TableViewer::description() const
00426 {
00427    return "the table viewer base class";
00428 }
00429 
00432 
00433 void TableViewer::help()
00434 {
00435 }
00436 
00439 
00440 
00441 bool TableViewer::is_dirty() const
00442 {
00443     return dataDirty_;
00444 }
00445 
00448 
00449 size_t TableViewer::longestRowTitle() const
00450 {
00451     size_t rv = 0;
00452 
00453     vector<RowInfo>::const_iterator first = rows_.begin(), last = rows_.end();
00454 
00455     while(first != last)
00456     {
00457         RowInfo const &cur = *first++;
00458 
00459         size_t length = cur.title_.size();
00460 
00461         if(length > rv)
00462           rv = length;
00463 
00464     }
00465 
00466     return rv;
00467 }
00468 
00471 size_t TableViewer::CellInfo::widestRow() const
00472 {
00473   vector<string>::const_iterator first = text_.begin(), last = text_.end();
00474 
00475   size_t rv = 0;
00476 
00477   while(first != last)
00478   {
00479     string const &cur = *first++;
00480 
00481     size_t length = cur.size();
00482 
00483     if(length > rv)
00484       rv = length;
00485 
00486   }
00487 
00488   return rv;
00489 }
00492 void TableViewer::setRowsAndColumns(size_t rows, size_t cols)
00493 {
00494     layout_.resize(rows);
00495 
00496     for(size_t i = 0; i  < layout_.size(); ++i)
00497     {
00498         layout_.resize(cols); // resize the cols to match the request.
00499     }
00500 
00501     rows_.resize(rows);
00502     cols_.resize(cols);
00503 
00504     recomputeLayout(); // regenerate world coords in all rows and cols
00505 
00506 
00507 }
00510 size_t TableViewer::columnWidth(size_t col) const
00511 {
00512     size_t rv = 0;
00513 
00514     if(cols_.size() > col)
00515     {
00516        rv = cols_[col].width_;
00517     }
00518 
00519     LayoutType::const_iterator firstRow = layout_.begin(), lastRow = layout_.end();
00520 
00521     while(firstRow != lastRow)
00522     {
00523        LayoutType::value_type const &curRow = *firstRow++;
00524 
00525        if(curRow.size() > col)
00526        {
00527           // this row has the specfied column
00528 
00529           size_t width = curRow[col].widestRow(); // in the multi-row cell
00530 
00531           if(width > rv)
00532             rv = width;
00533 
00534        }
00535     }
00536 
00537     return rv;
00538 
00539 }
00542 TableViewer::CellInfo const *
00543 TableViewer::cellInfo(size_t row, size_t col) const
00544 {
00545    return const_cast<TableViewer*>(this)->cellInfo(row,col);
00546 }
00547 
00550 TableViewer::CellInfo *
00551 TableViewer::cellInfo(size_t row, size_t col) 
00552 {
00553     if(row < rows())
00554         if(col < cols_.size())
00555         {
00556            // this row and column should exist -- but due to lazy
00557            // evaluation, mabye it does not -- make sure that the 
00558            // cell does before returning its addres.
00559 
00560            while(layout_.size() <= row)
00561            {
00562                layout_.push_back( LayoutType::value_type() ); // create rows  
00563            }
00564 
00565            while(layout_[row].size() <= col)
00566            {
00567              layout_[row].push_back( LayoutType::value_type::value_type() );
00568            }
00569 
00570            return &layout_[row][col];
00571         }
00572 
00573     return 0;
00574    
00575 }
00576 
00579 void TableViewer::recomputeLayout()
00580 {
00581    // the data for positions in the computed layout have to be adjusted to account
00582    // for titles and other adjustments before positioning the real cursor.
00583 
00584 
00585    if(!layoutDirty_)
00586       return;
00587 
00588    layoutDirty_ = false;
00589 
00590    LayoutType::iterator firstRow = layout_.begin(), lastRow = layout_.end();
00591 
00592    vector<size_t> colWidths_;
00593 
00594    // first, find out how wide the columns are
00595 
00596    for(size_t col=0; col < cols_.size(); ++col)
00597    {
00598       size_t colWidth = columnWidth(col);
00599 
00600       colWidths_.push_back(colWidth);
00601    }
00602 
00603    // next, assign virtual world row/col coords for each
00604 
00605    size_t worldY = 0;  // assume no titles or other issues of interest
00606 
00607    while(firstRow != lastRow)
00608    {
00609       LayoutType::value_type &curRow = *firstRow++;
00610 
00611       size_t worldX = 0;  // assume no titles
00612 
00613       curRow.resize( cols_.size() );
00614 
00615       LayoutType::value_type::iterator firstCol = curRow.begin();
00616       LayoutType::value_type::iterator lastCol = curRow.end();
00617 
00618       vector<size_t>::const_iterator widthPtr = colWidths_.begin(), widthEnd = colWidths_.end();
00619 
00620       size_t tallestCellHeight=1;
00621 
00622       while(firstCol != lastCol && widthPtr != widthEnd )
00623       {
00624          CellInfo &cell = *firstCol++;
00625 
00626          ++worldX; // skip the | or space gutter before each row
00627 
00628          cell.worldColumn_ = worldX;
00629          cell.worldRow_    = worldY;
00630 
00631          worldX += *widthPtr++;
00632 
00633          size_t cellHeight = cell.text_.size();
00634 
00635          if(cellHeight > tallestCellHeight)
00636              tallestCellHeight = cellHeight;
00637 
00638       }
00639 
00640       worldY +=  tallestCellHeight;
00641 
00642    }
00643 }
00644 
00647 void TableViewer::setCursorInfo(CursorWindow::viewport *vp,
00648                                 size_t r, 
00649                                 size_t c, 
00650                                 bool v)
00651 {
00652    if(r != cursorRow_ || c != cursorCol_ )
00653    {
00654        hideCursor(vp);  // marks it as un-displayed if it already is
00655    }
00656 
00657    cursorRow_       = r;
00658    cursorCol_       = c;
00659    cursorDisplayed_ = v;
00660 
00661    if(v)
00662         showCursor(vp);
00663 }
00664 
00667 void TableViewer::hideCursor(CursorWindow::viewport *vp)
00668 {
00669     cursorDisplayed_ = false;
00670 
00671     paintCursor(vp);
00672 
00673 }
00676 void TableViewer::showCursor(CursorWindow::viewport *vp)
00677 {
00678     cursorDisplayed_ = true;
00679     paintCursor(vp);
00680    
00681 }
00684 void TableViewer::paintCursor(CursorWindow::viewport *vp)
00685 {
00686     // redraw the cursor on the specified viewport.
00687 
00688     if(vp)
00689     {
00690 
00691        size_t cursorRowDelta = cursorRow_ - originRow();
00692        size_t cursorColDelta = cursorCol_ - originCol();
00693 
00694        if(cursorRowDelta < pageHeight_ && cursorColDelta < pageWidth_)
00695        {
00696           // ok, the cursor is visible on the screen
00697 
00698           size_t screenRowDelta = 0;
00699 
00700           size_t screenColDelta = 0;
00701 
00702           if(layout_.size() > cursorRow_ && layout_[cursorRow_].size() > cursorCol_)
00703           {
00704               CellInfo const &cursorCell = layout_[cursorRow_][cursorCol_];
00705               
00706               screenRowDelta = cursorCell.worldRow_ -
00707                                layout_[originRow()][originCol()].worldRow_;
00708               
00709               screenColDelta = cursorCell.worldColumn_ -
00710                                layout_[originRow()][originCol()].worldColumn_;
00711               
00712           }
00713 
00714           int screenRow = screenRowDelta + displayOrigin_.row_;
00715           int screenCol = screenColDelta + displayOrigin_.col_;
00716 
00717           setTextInputPointer(screenRow, screenCol+1); // one for the gutter
00718 
00719           paintCell(vp,
00720                     screenRow, 
00721                     screenCol, 
00722                     cursorRow_, 
00723                     cursorCol_, 
00724                     cursorDisplayed_
00725                    );
00726 
00727 
00728           positionTextInputPointer(vp);  // may have been changed by paint cell!
00729  
00730 
00731           // no nothing yet
00732 
00733        }
00734 
00735        // find the row and column on the screen and redraw the field there.
00736     }
00737 }
00738 
00739 static int textAtt(bool bold, bool underlined, bool reversed)
00740 {
00741    int rv = CursorWindow::normal;
00742 
00743    if(bold && underlined && reversed)
00744    {
00745       return CursorWindow::dialog_title_att;
00746    }
00747    else
00748    if(bold && underlined)
00749    {
00750        return CursorWindow::inactive_title_att;
00751    }
00752    else
00753    if(bold && reversed)
00754    {
00755        return CursorWindow::active_title_att;
00756    }
00757    else
00758    if(underlined && reversed)
00759    {
00760        return CursorWindow::active_mark_att;
00761    }
00762    else
00763    if(underlined)
00764    {
00765       return CursorWindow::bottom_att;
00766    }
00767    else
00768    if(reversed)
00769    {
00770        return CursorWindow::highlighted_att;
00771    }
00772    else
00773    if(bold)
00774    {
00775        return CursorWindow::inactive_mark_att;
00776    }
00777 
00778    return rv;
00779 
00780 }
00781 
00782 
00785 void TableViewer::paintCell(CursorWindow::viewport *vp,
00786                             size_t vpRow, 
00787                             size_t vpCol, 
00788                             size_t tbRow, 
00789                             size_t tbCol, 
00790                             bool highlight,
00791                             bool editCursor 
00792                            )
00793 {
00794     size_t vpColumns = colWidths_[tbCol];
00795 
00796     CellInfo const *cell = cellInfo(tbRow,tbCol);
00797 
00798     if(cell == 0)
00799        return;
00800 
00801     // position the cursor to the top left corner of the cell.
00802     // For each row, display the gutter, then display the row text
00803     // then pad with trailing blanks.
00804     //
00805     // right now just do one row
00806 
00807 
00808     vp->set_curpos(vpRow, vpCol);
00809 
00810     bool bold = rows_[tbRow].bold_  || cols_[tbCol].bold_;
00811 
00812     bool underline = rows_[tbRow].bottomBorder_;
00813 
00814 
00815     if(highlight && cursorDisplayed_)
00816     {
00817        vp->set_text_attribute(textAtt(bold, underline, true));
00818     }
00819     else
00820       vp->set_text_attribute(textAtt(bold, underline, false));
00821 
00822     if(cols_[tbCol].leftBorder_)
00823         *vp << "|"; // paint the gutter
00824     else
00825         *vp << " "; // paint the gutter
00826 
00827 
00828     paintField(vp, vpRow, vpCol, cell, 0, vpColumns, editCursor);
00829 
00830 
00831 }
00834 void TableViewer::paintField(CursorWindow::viewport      *vp,
00835                              size_t                       vpRow, 
00836                              size_t                       vpCol, 
00837                              TableViewer::CellInfo const *cell, 
00838                              size_t                       cellRow, 
00839                              size_t                       vpColumns,
00840                              bool                         ignored    // the base class doesn't support edit cursor
00841                             )
00842 {
00843     // get the length of the data to display
00844 
00845     char const *data = "";
00846     size_t      length= 0;
00847 
00848     if(cell)
00849     {
00850       if(cell->text_.size() > cellRow)
00851       {
00852         data = cell->text_[cellRow].data();
00853         length = cell->text_[cellRow].size();
00854       }
00855     }
00856 
00857     if(length > vpColumns)
00858     {
00859        length = vpColumns;
00860     }
00861 
00862     vp->write(data, length);
00863 
00864     if(length < vpColumns)
00865         vp->write(' ', vpColumns - length);
00866 }
00869 void TableViewer::moveUp(CursorWindow::viewport *vp)
00870 {
00871     if(cursorRow_ > originRow_)
00872     {
00873       setCursorInfo(vp, cursorRow_-1, cursorCol_, true);
00874       return;
00875     }
00876 
00877     // cursor was at top of screen, so if there are rows above this, display one more
00878     // than we currently see
00879 
00880 
00881     if(originRow())
00882     {
00883        size_t col = cursorCol_;
00884 
00885        hideCursor(vp);
00886 
00887        setOrigin( originRow() - 1, originCol() );
00888 
00889        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00890 
00891        setCursorInfo(vp, originRow(), col);
00892     }
00893 }
00896 void TableViewer::moveDown(CursorWindow::viewport *vp)
00897 {
00898 
00899     size_t pageDeltaRow = cursorRow_ - originRow();
00900 
00901     if(pageDeltaRow < pageHeight_-1)
00902     {
00903        setCursorInfo(vp, cursorRow_+1, cursorCol_, true);
00904        return;
00905     }
00906     
00907     if(rows() && cursorRow_ < rows()  )
00908     {
00909         size_t col = cursorCol_;
00910 
00911         hideCursor(vp);
00912 
00913         if(originRow() < rows()-1)
00914             setOrigin( originRow() + 1, originCol() );
00915         else
00916             setOrigin( rows()-1, originCol() );
00917 
00918         (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00919 
00920         // pageHeight_ will have been recomputed. as will origin row and column
00921 
00922         setCursorInfo(vp, originRow() + pageHeight_-1, col ); 
00923     }
00924 
00925 
00926 
00927 }
00930 void TableViewer::moveLeft(CursorWindow::viewport *vp)
00931 {
00932     if(cursorCol_ > originCol())
00933     {
00934        setCursorInfo(vp, cursorRow_, cursorCol_ - 1);
00935        return;
00936     }
00937 
00938     if(originCol())
00939     {
00940        size_t row = cursorRow_;
00941 
00942        hideCursor(vp);
00943 
00944        setOrigin( originRow(), originCol() - 1 );
00945 
00946        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00947 
00948        setCursorInfo(vp, row, originCol());
00949 
00950     }
00951 }
00954 void TableViewer::moveRight(CursorWindow::viewport *vp)
00955 {
00956     if(cursorCol_ < originCol() + pageWidth_ -1)
00957     {
00958         setCursorInfo(vp, cursorRow_, cursorCol_ +1);
00959         return;
00960     }
00961 
00962     // we must have been sitting on the right side of the screen
00963 
00964     if(originCol() < cols_.size()-1)
00965     {
00966        size_t row = cursorRow_;
00967 
00968        hideCursor(vp);
00969 
00970        setOrigin( originRow(), originCol() + 1 );
00971 
00972        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00973 
00974        setCursorInfo(vp, row, originCol() + pageWidth_ -1);
00975     }
00976 }
00977 
00980 void TableViewer::pageUp(CursorWindow::viewport *vp)
00981 {
00982     size_t pageHeight = vp->size().row_-1;
00983 
00984     {
00985        size_t savedCol = cursorCol_;
00986 
00987        if(originRow() < pageHeight)
00988        {
00989           setOrigin( 0, originCol() );
00990        }
00991        else
00992            setOrigin( originRow() - pageHeight, originCol() );
00993 
00994        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
00995 
00996        setCursorInfo(vp, originRow(), savedCol);
00997     }
00998 }
01001 void TableViewer::pageDown(CursorWindow::viewport *vp)
01002 {
01003     size_t pageHeight = vp->size().row_-1;
01004 
01005     if(rows() && originRow() < layout_.size())
01006     {
01007        size_t targetRow = originRow() + pageHeight;
01008 
01009        if(  originRow() >= layout_.size()-1-pageHeight )
01010        {
01011            targetRow = layout_.size()-1;
01012        }
01013 
01014        size_t savedCol = cursorCol_;
01015 
01016        if(targetRow >= layout_.size())
01017          {
01018            if(layout_.size() == 0)
01019              targetRow = 0;
01020            else
01021              targetRow = layout_.size()-1;
01022          }
01023 
01024        setOrigin( targetRow, originCol() );
01025 
01026        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01027 
01028        setCursorInfo(vp, originRow(), savedCol);
01029     }
01030 
01031 }
01034 void TableViewer::pageRight(CursorWindow::viewport *vp)
01035 {
01036     if(cols_.size())
01037     {
01038         size_t newOrigin = originCol() + pageWidth_-1;
01039 
01040         if(newOrigin >= cols_.size())
01041         {
01042            // size is known not to be zero
01043 
01044            newOrigin = cols_.size()-1;
01045 
01046         }
01047 
01048         size_t colDelta = cursorCol_ - originCol_;
01049 
01050         if(newOrigin + colDelta >= cols_.size())
01051         {
01052            colDelta = 0;
01053         }
01054 
01055         size_t row = cursorRow_;
01056         
01057         hideCursor(vp);
01058         
01059         setOrigin( originRow(), newOrigin );
01060         
01061         (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01062         
01063         setCursorInfo(vp, row, originCol() + colDelta);
01064     }
01065 }
01066 
01069 void TableViewer::pageLeft(CursorWindow::viewport *vp)
01070 {
01071     size_t colDelta = cursorCol_ - originCol();
01072     size_t row      = cursorRow_;
01073 
01074     if(originCol() > pageWidth_-1)
01075     {
01076        size_t pageDelta = pageWidth_-1;
01077 
01078        if(pageDelta == 0 && originCol() > pageDelta)
01079            pageDelta = 1;
01080 
01081        hideCursor(vp);
01082 
01083        setOrigin( originRow(), originCol() - pageDelta );
01084 
01085        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01086 
01087        setCursorInfo(vp, row, originCol() + colDelta);
01088 
01089     }
01090     else
01091     {
01092        hideCursor(vp);
01093        setOrigin(originRow(), 0);
01094        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01095        setCursorInfo(vp, row, 0);
01096     }
01097 }
01098 
01101 void TableViewer::pageHome(CursorWindow::viewport *vp)
01102 {
01103     setOrigin( 0, 0);
01104 
01105     (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01106 }
01107 
01110 void TableViewer::pageEnd(CursorWindow::viewport *vp)
01111 {
01112     if(cols_.size())
01113     {
01114        setOrigin( originRow(), cols_.size() - 1 );
01115 
01116        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01117     }
01118 }
01121 void TableViewer::pageBottom(CursorWindow::viewport *vp)
01122 {
01123     if(rows())
01124     {
01125        size_t savedCursorCol = cursorCol_;
01126        size_t targetRow      = rows()-1;
01127 
01128        setOrigin( targetRow, originCol());
01129 
01130        (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01131 
01132        setCursorInfo(vp, targetRow, savedCursorCol);
01133 
01134     }
01135 }
01138 void TableViewer::pageTop(CursorWindow::viewport *vp)
01139 {
01140     size_t savedCursorCol = cursorCol_;
01141 
01142     setOrigin(0, originCol());
01143 
01144     (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01145 
01146     setCursorInfo(vp, 0, savedCursorCol);
01147 
01148 }
01151 void TableViewer::clear()
01152 {
01153    rows_.clear();
01154    cols_.clear();
01155    layout_.clear();
01156    colWidths_.clear();
01157 
01158    setCursorInfo(0,0,0);
01159    setOrigin(0,0);
01160 
01161 }
01164 void TableViewer::positionTextInputPointer(CursorWindow::viewport *vp)
01165 {
01166     vp->refresh();
01167     vp->set_curpos(textInputPointer_.row_, textInputPointer_.col_); // 1 for the gutter
01168 }
01169 
01172 void TableViewer::insertRowAbove(CursorWindow::viewport *vp, RowInfo const &info)
01173 {
01174    LayoutType::iterator rowPointer = layout_.begin() + cursorRow_;
01175 
01176    layout_.insert(rowPointer, LayoutType::value_type());
01177    
01178    for(size_t col=0; col < cols_.size(); ++col)
01179    {
01180        layout_[cursorRow_].push_back(CellInfo()); // create empty cells
01181 
01182        layout_[cursorRow_].back().text_.push_back(""); // 1 empty row of text.
01183    }
01184 
01185    vector<RowInfo>::iterator rowIterator = rows_.begin()+cursorRow_;
01186 
01187    rows_.insert(rowIterator, info);
01188 
01189    forceRelayout();
01190 
01191 }
01194 void TableViewer::insertRowBelow(CursorWindow::viewport *vp, RowInfo const &info)
01195 {
01196    if(cursorRow_ < layout_.size()-1)
01197    {
01198       // unless we are at the bottom, step down to the next row and insert
01199       // the new line above the line below
01200 
01201       ++cursorRow_;
01202 
01203       insertRowAbove(vp,info);
01204    }
01205    else
01206    {
01207        layout_.push_back(LayoutType::value_type());
01208 
01209        for(size_t col=0; col < cols_.size(); ++col)
01210        {
01211            layout_[cursorRow_].push_back(CellInfo()); // create empty cells
01212     
01213            layout_[cursorRow_].back().text_.push_back(""); // 1 empty row of text.
01214        }
01215 
01216        vector<RowInfo>::iterator rowIterator = rows_.begin()+cursorRow_;
01217 
01218        rows_.insert(rowIterator, info);
01219 
01220        ++cursorRow_;
01221 
01222     
01223        forceRelayout();
01224     
01225 
01226 
01227    }
01228 }
01231 void TableViewer::RowInfo::setRowTitleNumber(size_t row)
01232 {
01233     char buffer[40];
01234 
01235     sprintf(buffer, "%lu", (unsigned long)row);
01236 
01237     title_ = buffer;
01238 }
01239 
01242 void TableViewer::deleteRow(CursorWindow::viewport *vp)
01243 {
01244    // delete the current cursor row
01245 
01246    if(cursorRow_ < layout_.size())
01247    {
01248       LayoutType::iterator rowIterator = layout_.begin() + cursorRow_;
01249 
01250       layout_.erase(rowIterator);
01251 
01252       forceRelayout();
01253    }
01254 }
01255 
01256 
01257 void TableViewer::findFirst(CursorWindow::viewport *vp)
01258 {
01259   CursorWindow::Dialog dialog("Find string");
01260 
01261   char const *mix = "Y";
01262 
01263   if(mixed_case_ == false)
01264     mix = "N";
01265 
01266   dialog += new CursorWindow::Dialog::String("case",
01267                                              "Mixed case",
01268                                              mix,
01269                                              2
01270                                             );
01271 
01272 
01273   dialog += new CursorWindow::Dialog::String("mode",
01274                                              "(W)ord, (R)egexp, or (S)tring",
01275                                              (is_regex_ ? "R" : (match_words_ ? "W" : "S")),
01276                                              2
01277                                             );
01278 
01279   dialog += new CursorWindow::Dialog::String("text",
01280                                              "Search for",
01281                                              search_text_,
01282                                              40
01283                                             );
01284 
01285   dialog += new CursorWindow::Dialog::Ok("ok","");
01286 
01287   dialog.set_first_input_field("text");
01288 
01289 
01290   if( dialog.popup(vp->window()) )
01291     return;
01292 
01293   std::string mixed_case = dialog.element_value("case");
01294 
01295   if(   mixed_case.size() 
01296      && (    mixed_case[0] == 'n'
01297          ||  mixed_case[0] == 'N'
01298         )
01299     )
01300   {
01301     mixed_case_ = false;
01302   }
01303   else
01304   if(    mixed_case.size() 
01305       && (mixed_case[0] == 'y' ||  mixed_case[0] == 'Y')
01306     )
01307   {
01308     mixed_case_ = true;
01309   }
01310 
01311   std::string match_words = dialog.element_value("mode");
01312 
01313   match_words_ = false;
01314   is_regex_    = false;
01315 
01316   if(   match_words.size() 
01317      && (    match_words[0] == 'r'
01318          ||  match_words[0] == 'R'
01319         )
01320     )
01321   {
01322     is_regex_ =  true;
01323   }
01324   else
01325   if(    match_words.size() 
01326      &&  (match_words[0] == 'w' ||  mixed_case[0] == 'W')
01327     )
01328   {
01329     match_words_ = true;
01330   }
01331 
01332   std::string search_text = dialog.element_value("text");
01333 
01334   if(search_text.size() == 0)
01335     return; // ignore searches for nothing
01336 
01337   search_text_ = search_text;
01338 
01339   findNext(vp, true);
01340 
01341 }
01342 
01343 
01344 struct CharCompare
01346 {
01347   TableViewer const *impl_;
01348 
01349   bool operator() (char a, char b) const
01350   {
01351       if(impl_->mixed_case())
01352       {
01353         if( isupper(a) )
01354           a = tolower(a);
01355       
01356         if( isupper(b) )
01357           b = tolower(b);
01358       
01359         return a == b;
01360       }
01361       
01362       return a == b;
01363   }
01364 
01365   CharCompare(TableViewer const* t)
01366   : impl_(t)
01367   {
01368   }
01369 
01370 };
01371 
01372 
01373 bool TableViewer::findNext(CursorWindow::viewport *vp, bool first)
01374 {
01375    size_t row = cursorRow_;
01376    size_t col = cursorCol_;
01377 
01378 
01379    if(!first)
01380        ++col;
01381 
01382    SimpleRegex   regex(search_text_,  (mixed_case_ ? "i" : "") );
01383 
01384 
01385    while(row < layout_.size())
01386    {
01387        while(col < layout_[row].size())
01388        {
01389            if(layout_[row][col].text_.empty())
01390            {
01391               ++col;
01392               continue;
01393            }
01394            
01395            string const &cell = layout_[row][col].text_[0];
01396            
01397            bool found = false;
01398 
01399            if(is_regex_)
01400            {
01401                if( regex(cell, 0, cell.size() ) != 0)
01402                  found = true;
01403            }
01404            else
01405            {
01406                string::const_iterator match;
01407 
01408                string::const_iterator searchStart = cell.begin();
01409                string::const_iterator searchEnd   = cell.end();
01410 
01411                do
01412                {
01413                    match=search(searchStart, 
01414                                 searchEnd, 
01415                                 search_text_.begin(), 
01416                                 search_text_.end(),
01417                                 CharCompare(this)
01418                                );
01419 
01420                    if(match_words_)
01421                    {
01422                        if(match != searchEnd)
01423                        {
01424                          found = true;
01425                        
01426                          // we have found a match -- but if we are searching for
01427                          // words, it does not count as a match unless it is surrounded
01428                          // by word characters (or the bounds of the search space
01429                        
01430                          if(match != cell.begin())
01431                          {
01432                             string::const_iterator before = match; --before;
01433                        
01434                             if(isalnum(*before))
01435                             {
01436                               found = false;
01437                               ++searchStart;
01438                             }
01439                          }
01440                        
01441                          if(found)
01442                          {
01443                              string::const_iterator end = match+search_text_.size();
01444                        
01445                              if(end != searchEnd)
01446                              {
01447                                  if(isalnum(*end))
01448                                  {
01449                                    found = false;
01450                                    ++searchStart;
01451                                  }
01452                              }
01453                        
01454                          }
01455                        }
01456                    }
01457                    else
01458                    if(match != searchEnd)
01459                      found=true;
01460                }
01461                while(!found && match != searchEnd);
01462                
01463 
01464            }
01465 
01466            if(found)
01467            {
01468            
01469                hideCursor(vp);
01470 
01471                if(    row >= originRow() + pageHeight_
01472                   ||  col >= originCol() + pageWidth_
01473                   ||  col <  originCol()
01474                  )
01475                {
01476                    size_t rowBackoff = pageHeight_ / 2;
01477                    size_t colBackoff = pageWidth_  / 2;
01478 
01479                    size_t targetRow = row;
01480                    size_t targetCol = col;
01481 
01482 
01483                    if(targetRow > rowBackoff)
01484                       targetRow = row - rowBackoff;
01485                    else
01486                       targetRow = 0;
01487                   
01488                    if(targetCol > colBackoff)
01489                       targetCol = col - colBackoff;
01490                    else
01491                       targetCol = 0;
01492 
01493 
01494                    setOrigin(targetRow, targetCol);
01495 
01496                    (*this)(vp, CursorWindow::viewport::repaint_handler::activate);
01497                }
01498            
01499                setCursorInfo(vp, row, col);
01500            
01501               return true;
01502            }
01503 
01504            ++col;
01505        }
01506 
01507        ++row;
01508        col=0;
01509    }
01510 
01511    vp->beep();
01512 
01513    return false;
01514 
01515 }
01516 } // namespace cxxtls
Generated on Wed Feb 29 22:50:05 2012 for CXXUtilities by  doxygen 1.6.3