cursorwindow.cxx

Go to the documentation of this file.
00001 //
00002 // Copyright 2002, 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 
00029 
00030 #include <cxxtls/foreach.h>
00031 #include <cxxtls/cursorwindow.h>
00032 #include <cxxtls/cursesinterface.h>
00033 #include <cxxtls/strtool.h>
00034 #include <cxxtls/file.h>
00035 #include <cxxtls/viewer.h>
00036 #include <portable_io.h>
00037 #include <set>
00038 
00039 namespace cxxtls
00040 {
00041 
00042 using namespace CursesInterface;
00043 
00044 int CursorWindow::normal_att        = CursorWindow::normal;
00045 int CursorWindow::active_title_att  = CursorWindow::reverse_bold;
00046 int CursorWindow::inactive_title_att= CursorWindow::bold_ul;
00047 int CursorWindow::active_mark_att   = CursorWindow::reverse_ul;  // highlight plus mark
00048 int CursorWindow::inactive_mark_att = CursorWindow::bold;        // mark but not highlight
00049 int CursorWindow::bottom_att        = CursorWindow::underlined;  // bottom line of viewport
00050 int CursorWindow::highlighted_att   = CursorWindow::reversed;    // highlight but not mark
00051 int CursorWindow::dialog_normal_att = CursorWindow::reverse_bold;
00052 int CursorWindow::dialog_title_att  = CursorWindow::rev_ul_bold;
00053 
00054 typedef CursorWindow::row_col     row_col;
00055 typedef std::string               string;
00056 typedef CursorWindow::viewport    viewport;
00057 
00058 const long WScale = 10000;  
00059 
00060 
00061 
00062                         
00063 const long WHalfCell = (WScale / 2) - 1 ;
00064 
00065 struct viewport::port
00066   //
00068 {
00069   row_col            scaled_origin_;  // real origin * WScale
00070   row_col            scaled_size_;    // real size   * WScale
00071   row_col            curpos_;
00072   CursorWindow*      window_;
00073   int                current_attribute_;
00074   repaint_handler*   handler_;
00075   viewport::position position_;
00076 
00077   port(CursorWindow* w, viewport::repaint_handler* h, viewport::position p)
00078   : curpos_(0,0),
00079     window_(w),
00080     current_attribute_(CursorWindow::normal),
00081     handler_(h),
00082     position_(p)
00083   {
00084     scaled_origin_.row_ = 0;  // 0 times 10,000
00085     scaled_origin_.col_ = 0;  // 0 times 10,000
00086 
00087     scaled_size_.row_ = w->size().row_ * WScale;
00088     scaled_size_.col_ = w->size().col_ * WScale;
00089 
00090   }
00091 
00092   row_col size()
00095   {
00096     row_col rv(scaled_size_);
00097 
00098     rv.row_ /= WScale;
00099     rv.col_ /= WScale;
00100 
00101     return rv;
00102 
00103   }
00104 
00105   row_col origin()
00108   {
00109     row_col rv(scaled_origin_);
00110 
00111     rv.row_ /= WScale;
00112     rv.col_ /= WScale;
00113 
00114     return rv;
00115 
00116   }
00117 
00118 };
00119 
00120 #define port            viewport::port
00121 #define repaint_handler viewport::repaint_handler
00122 
00123 
00124 struct compare_port_by_row_col
00125 {
00126   bool operator() (port* a, port* b)
00127   {
00128     if(a == b)
00129       return false;
00130 
00131     int a_row = a->scaled_origin_.row_;
00132     int b_row = b->scaled_origin_.row_;
00133 
00134     if(a_row < b_row)
00135       return true;
00136 
00137     if(a_row > b_row)
00138       return false;
00139 
00140     // a and b originate on the same row
00141 
00142     int a_col = a->scaled_origin_.col_;
00143     int b_col = b->scaled_origin_.col_;
00144 
00145     if(a_col < b_col)
00146       return true;
00147 
00148     return false;
00149 
00150 
00151   }
00152 };
00153 
00154 struct compare_port_by_col_row
00155 {
00156   bool operator() (port* a, port* b)
00157   {
00158     if(a == b)
00159       return false;
00160 
00161     int a_col = a->scaled_origin_.col_;
00162     int b_col = b->scaled_origin_.col_;
00163 
00164     if(a_col < b_col)
00165       return true;
00166 
00167     if(a_col > b_col)
00168       return false;
00169 
00170     // a and b originate on the same col
00171 
00172     int a_row = a->scaled_origin_.row_;
00173     int b_row = b->scaled_origin_.row_;
00174 
00175     if(a_row < b_row)
00176       return true;
00177 
00178     return false;
00179 
00180   }
00181 };
00182 
00183 
00184 
00185 
00186 
00187 
00188 
00189 class CursorWindow::Window
00190   //
00195   //
00196 {
00197 public:
00198 
00199    int                     current_attribute_;
00200    row_col                 curpos_;
00201    row_col                 size_;
00202    char const           *  error_;
00203    bool                    open_;
00204    resize_handler  *       resize_handler_;
00205 
00206    port*                   viewport_[100];
00207    int                     active_viewport_;
00208    int                     viewports_;
00209    int                     needs_resized_; 
00210 
00211    Window()      
00212    : current_attribute_(normal),
00213      curpos_(0,0),
00214      size_(0,0),
00215      error_(0),
00216      open_(0),
00217      resize_handler_(0),
00218      active_viewport_(0),
00219      viewports_(0),
00220      needs_resized_(0)
00221    {
00222 
00223    }
00224 
00225    port* new_port(CursorWindow* w, repaint_handler* h, viewport::position p)
00226    {
00227      port* n = new port(w,h,p);
00228 
00229      viewport_[viewports_++] = n;
00230 
00231      return n;
00232 
00233    }
00234 
00235    void cover_hole(port* deceased)
00237    {
00238      // 'deceased' must not have been deleted till after this function returns!
00239 
00240      // This algorithm ignores floating viewports!
00241 
00242      // resize other viewports to close the hole left by deleting port p.
00243      // this function doesn't delete p, it just rescales the remaining ports.
00244      // The assumption is that p has NOT yet been deleted.
00245      //
00246      // Here are the cases of interest:
00247      //
00248      //   One viewport being deleted:  (case 1)
00249      //
00250      //       +-------------+
00251      //       |             |      -- no viewports are left
00252      //       +-------------+
00253      //
00254      //   Horizontally split viewport with one being deleted: (case 2)
00255      //
00256      //       +------+------+
00257      //       |      |      |      -- one viewport is left -- it gets the whole
00258      //       +------+------+         window
00259      //
00260      //   Vertically split viewport with one being deleted: (another case 2)
00261      //
00262      //       +-------------+
00263      //       |             |      -- one viewport is left, it gets the whole
00264      //       +-------------+         screen
00265      //       |             |
00266      //       +-------------+
00267      //
00268      //   Three splits become two, when viewport X is deleted: (case 3)
00269      //
00270      //       +-----+-----+----+
00271      //       |  Y  |  X  |    |            And the resultant is:
00272      //       +-----+-----+----+
00273      //                                 +--------+----+
00274      //                                 |   Y    |    |
00275      //                                 +--------+----+
00276      //
00277      //   In general with simple splits like the above, one viewport is deleted
00278      //   and the deleted space goes to another viewport.  Even if there 12
00279      //   horizontal or vertical splits.  In general, ONE viewport of the remaining
00280      //   set is given the space vacated by the deleted
00281      //
00282      //   Here is a more complex case:  (case 4)
00283      //
00284      //       +-----+-----+----+   -- In this case, viewport X is being deleted
00285      //       |     |  y  |    |      and the space in X given to 'y':
00286      //       |     +-----+    |
00287      //       |     |  X  |    |          +------+-------+------+  'resultant'
00288      //       +-----+-----+----+          |      |       |      |
00289      //       |                |          |      |   Y   |      |
00290      //       +----------------+          |      |       |      |
00291      //                                   +------+-------+------+
00292      //                                   |                     |
00293      //                                   +---------------------+
00294      //
00295      //  As you can see, an attempt is made to give all the freed space to a
00296      //  single window if possible.  The receiving viewport must contguous
00297      //  with and identical in either width or height so that it can exactly
00298      //  absorb the freed space.
00299      //
00300      //  However, this isn't always possible.  Consider, the deletion of D:
00301      //  (case 5)
00302      //
00303      //   +----+----+----+
00304      //   |    |    |    |      And the resultant is:
00305      //   +----+----+----+
00306      //   |       D      |         +----+----+----+
00307      //   +--------------+         |    |    |    |
00308      //                            |    |    |    |
00309      //                            |    |    |    |
00310      //                            +----+----+----+
00311      //  In this case, the deleted viewports space is given to ALL windows that
00312      //  end at the boundary of the deleted viewport.  Here is a more complex
00313      //  example where Q is being deleted: (case 6)
00314      //
00315      //     +----+----+
00316      //     |    |    |     And the resultant is:
00317      //     +----+----+
00318      //     |    Q    |      +-----+-----+
00319      //     +----+----+      |     |     |
00320      //     |    |    |      |     |     |
00321      //     +----+----+      |     |     |
00322      //                      |     |     |
00323      //                      +-----+-----+
00324      //                      |     |     |
00325      //                      +-----+-----+
00326      //
00327      //  In all cases where a viewport's space cannot be given to a single
00328      //  beneficiary, the dying window will have been one that spans the
00329      //  either the full width or the full height -- otherwise the viewport
00330      //  that took up the remainder thereof will be the beneficiary.
00331      //
00332      //  The following algorithm handles deletions in the above cases:
00333      //
00334      //    Start in the upper left corner viewport
00335      //
00336      //    Walk horizontally trying to give space to the viewport to the
00337      //    left -- or the right if there is no left -- if you can find
00338      //    a receipient which whose origin in the same row and whose height
00339      //    is the same. If you find a receipient, give it all the space
00340      //    and return from the function.  Repeat this for every row on which
00341      //    a tiled viewport's origin can be found.
00342      //
00343      //    If you have gotten to the lower right corner of the screen,
00344      //    repeat the above algorithm only walk from the upper left corner to
00345      //    the the lower right in a "column major" order instead of a row major
00346      //    order.
00347      //
00348      //    If you don't escape from the algorithm, it must be because the
00349      //    deceased port spans the complete width or complete height of the
00350      //    cursorwindow.  If this is true find all windows to the left or
00351      //    above, depending and give all the vertical or horizontal space to
00352      //    the remaining windows.  See cases 5 and 6 above.
00353 
00354      typedef std::set<port*, compare_port_by_row_col> row_major_set_type;
00355      typedef std::set<port*, compare_port_by_col_row> col_major_set_type;
00356 
00357      row_major_set_type rowset;
00358      col_major_set_type colset;
00359 
00360      int i;
00361 
00362      for(i=0; i < viewports_; ++i)
00363      {
00364         port* p = viewport_[i];
00365         
00366         if(p->position_ == viewport::tiled)
00367         {
00368           rowset.insert(p);
00369           colset.insert(p);
00370         }
00371         
00372      }
00373 
00374      // At this point, we two lists of all the 'tiled' windows.  The lists are
00375      // sorted 1) by the row first then the column -- leading to a row-major
00376      // iteration scheme, and 2) by the col first and then the row.
00377 
00378      // FIRST: iterate over the row major list and try to give all the freed
00379      //        space to the row that is the same height, begins in the same
00380      //        row and is immediately to the left of the deceased.
00381 
00382      row_col origin;
00383      row_col size;
00384 
00385      origin.row_ = deceased->scaled_origin_.row_ / WScale;
00386      origin.col_ = deceased->scaled_origin_.col_ / WScale;
00387      size.row_   = deceased->scaled_size_.row_   / WScale;
00388      size.col_   = deceased->scaled_size_.col_   / WScale;
00389 
00390      row_major_set_type::iterator rowscan;
00391      col_major_set_type::iterator colscan;
00392 
00393      for(rowscan = rowset.begin(); rowscan != rowset.end(); ++rowscan)
00394      {
00395        port *cur = *rowscan;
00396 
00397        row_col curorg;
00398        row_col cursiz;
00399 
00400        curorg.row_ = cur->scaled_origin_.row_  / WScale;
00401        curorg.col_ = cur->scaled_origin_.col_  / WScale;
00402        cursiz.row_ = cur->scaled_size_.row_    / WScale;
00403        cursiz.col_ = cur->scaled_size_.col_    / WScale;
00404 
00405        if(curorg.row_ != origin.row_)
00406          continue;
00407 
00408        if(origin.col_ == 0)
00409        {
00410          // give my space to the viewport to the right
00411         
00412          if(curorg.col_ == (origin.col_ + size.col_)
00413             &&
00414             cursiz.row_ == size.row_
00415            )
00416          {
00417            // the current item begins immediately to the right of the
00418            // deceased and is the same height -- thus it receives the
00419            // freed space.
00420         
00421            cur->scaled_origin_.col_ = origin.col_ * WScale;
00422            cur->scaled_size_.col_  += size.col_   * WScale;
00423         
00424            return; // yeah baby, YEAH!
00425         
00426          }
00427         
00428        }
00429        else
00430        {
00431          // give my space to the viewport to the left
00432         
00433          if(origin.col_ == (curorg.col_ + cursiz.col_)
00434             &&
00435             size.row_ == cursiz.row_
00436            )
00437          {
00438            // the current item ends immediately to the left of the
00439            // deceased and is the same height -- thus it receives the
00440            // freed space.
00441         
00442            cur->scaled_size_.col_  += size.col_ * WScale;
00443         
00444            return; // yeah baby, YEAH!
00445          }
00446         
00447         
00448        }
00449      }
00450 
00451      // Ok, if we get here, it means that there is no window of the same
00452      // height as the deceased that begins on the same row and is strictly
00453      // contiguous with the deceased.  Now look for a window that occupies
00454      // exactly the same column as the decedent is contiguous therewith.
00455 
00456      for(colscan = colset.begin(); colscan != colset.end(); ++colscan)
00457      {
00458        port *cur = *colscan;
00459 
00460        row_col curorg;
00461        row_col cursiz;
00462 
00463        curorg.row_ = cur->scaled_origin_.row_  / WScale;
00464        curorg.col_ = cur->scaled_origin_.col_  / WScale;
00465        cursiz.row_ = cur->scaled_size_.row_    / WScale;
00466        cursiz.col_ = cur->scaled_size_.col_    / WScale;
00467 
00468        if(curorg.col_ != origin.col_)
00469          continue;
00470 
00471        if(origin.row_ == 0)
00472        {
00473          // give my space to the viewport below
00474         
00475          if(curorg.row_ == (origin.row_ + size.row_)
00476             &&
00477             cursiz.col_ == size.col_
00478            )
00479          {
00480            // the current item begins immediately to the right of the
00481            // deceased and is the same height -- thus it receives the
00482            // freed space.
00483         
00484            cur->scaled_origin_.row_ = origin.row_ * WScale;
00485            cur->scaled_size_.row_  += size.row_   * WScale;
00486         
00487            return; // yeah baby, YEAH!
00488         
00489          }
00490         
00491        }
00492        else
00493        {
00494          // give my space to the viewport above
00495         
00496          if(origin.row_ == (curorg.row_ + cursiz.row_)
00497             &&
00498             size.col_ == cursiz.col_
00499            )
00500          {
00501            // the current item ends immediately to the left of the
00502            // deceased and is the same height -- thus it receives the
00503            // freed space.
00504         
00505            cur->scaled_size_.row_  += size.row_  * WScale;
00506         
00507            return; // yeah baby, YEAH!
00508          }
00509         
00510         
00511        }
00512      }
00513 
00514      // OK, at this point, we have no best match to receive the freed
00515      // space -- so try to handle the case where the decedent is either
00516      // exactly as wide or exactly as tall as the cursor window.
00517 
00518      for(rowscan = rowset.begin(); rowscan != rowset.end(); ++rowscan)
00519      {
00520        //
00521        // This could just as easily have been colscan instead of rowscan
00522        // because the algorithm below isn't interested in rowmajor or
00523        // column majorprocessing.
00524        //
00525 
00526        port *cur = *rowscan;
00527 
00528        row_col curorg;
00529        row_col cursiz;
00530 
00531        curorg.row_ = cur->scaled_origin_.row_  / WScale;
00532        curorg.col_ = cur->scaled_origin_.col_  / WScale;
00533        cursiz.row_ = cur->scaled_size_.row_    / WScale;
00534        cursiz.col_ = cur->scaled_size_.col_    / WScale;
00535 
00536        if(size_.row_ == size.row_)
00537        {
00538          // the decedent occupies an entire column
00539         
00540          if(origin.col_ == 0)
00541          {
00542            // widen viewports at the right to occupy the missing space
00543         
00544            if(curorg.col_ == size.col_)
00545            {
00546              cur->scaled_origin_.col_ = 0;
00547              cur->scaled_size_.col_  += WScale * size.col_;
00548            }
00549         
00550          }
00551          else
00552          {
00553            // widen viewports to the left to occupy the missing space
00554         
00555            if(curorg.col_ + cursiz.col_ == origin.col_)
00556            {
00557              cur->scaled_size_.col_ += WScale * size.col_;
00558            }
00559         
00560          }
00561        }
00562        else
00563        if(size_.col_ == size.col_)
00564        {
00565          if(origin.row_ == 0)
00566          {
00567            // widen viewports below to occupy the missing space
00568         
00569            if(curorg.row_ == size.row_)
00570            {
00571              cur->scaled_origin_.row_ = 0;
00572              cur->scaled_size_.row_  += WScale * size.row_;
00573            }
00574         
00575          }
00576          else
00577          {
00578            // widen viewports above to occupy the missing space
00579         
00580            if(curorg.row_ + cursiz.row_ == origin.row_)
00581            {
00582              cur->scaled_size_.row_ += WScale * size.row_;
00583            }
00584         
00585          }
00586        }
00587        else
00588        {
00589          // the decedent does not consume an entire row or column
00590          // so we are in a case like this:
00591          //
00592          //   +---------+
00593          //   |    T    |   When viewport, DD, is deleted, its
00594          //   +----+----+   space must be given to A and B.
00595          //   | A  |    |
00596          //   +----+ DD |
00597          //   | B  |    |
00598          //   +----+----+
00599          //
00600          // As you can see, the reason A and B get space but not T
00601          // is that the height of A and B are totally within the range
00602          // of DD, where T's width only partially overlaps DD.  Further
00603          // A begins on a row common to DD and B ends on such a row.
00604          //
00605          // Thus, the rules for giving space to a viewport is this:
00606          //
00607          // The candidate must share a common edge with the decedent
00608          // and candidate must not be wider or taller than the decdent
00609          // and the decedent.  More than one candidate can get space.
00610          //
00611          //
00612         
00613          if( ( (curorg.row_ == origin.row_)                   ||
00614                (curorg.row_ + cursiz.row_) == (origin.row_ + size.row_)
00615              )
00616              &&
00617              (
00618                (curorg.col_ + cursiz.col_) == origin.col_  ||
00619                (origin.col_ + size.col_)   == curorg.col_
00620              )
00621              &&
00622              (
00623                (curorg.row_ >= origin.row_) &&
00624                (curorg.row_ +  cursiz.row_) <= (origin.row_ + size.row_)
00625              )
00626            )
00627          {
00628              // give width to 'cur'
00629         
00630              if(curorg.col_ == origin.col_ + size.col_)
00631              {
00632                // widen to the left
00633         
00634                cur->scaled_origin_.col_ = origin.col_ * WScale;
00635                cur->scaled_size_.col_  += size.col_   * WScale;
00636         
00637              }
00638              else
00639              {
00640                cur->scaled_size_.col_ += size.col_ * WScale;
00641              }
00642         
00643          }
00644          else
00645          if( ( (curorg.col_ == origin.col_)                   ||
00646                (curorg.col_ + cursiz.col_) == (origin.col_ + size.col_)
00647              )
00648              &&
00649              (
00650                (curorg.row_ + cursiz.row_) == origin.row_  ||
00651                (origin.row_ + size.row_)   == curorg.row_
00652              )
00653              &&
00654              (
00655                (curorg.col_ >= origin.col_) &&
00656                (curorg.col_ +  cursiz.col_) <= (origin.col_ + size.col_)
00657              )
00658            )
00659          {
00660              // give height to 'cur'
00661         
00662              if(curorg.col_ == origin.col_ + size.col_)
00663              {
00664                // widen to the top
00665         
00666                cur->scaled_origin_.row_ = origin.row_ * WScale;
00667                cur->scaled_size_.row_  += size.row_   * WScale;
00668         
00669              }
00670              else
00671              {
00672                cur->scaled_size_.row_ += size.row_ * WScale;
00673              }
00674          }
00675         
00676        }
00677      }
00678 
00679      // hopefully we caught it!
00680 
00681    }
00682 
00683    void delete_port(port* p)
00684    {
00685      int i;
00686 
00687      needs_resized_ = true;
00688 
00689      cover_hole(p);
00690 
00691      for(i=0; i < viewports_; ++i)
00692      {
00693        if(viewport_[i] == p)
00694        {
00695 
00696          // remove this port from the list and delete it.
00697         
00698          delete p;
00699         
00700          while(i < viewports_)
00701          {
00702            viewport_[i] = viewport_[i+1];
00703            ++i;
00704          }
00705         
00706          --viewports_;
00707         
00708          active_viewport_ = viewports_-1;
00709         
00710          if(viewports_)
00711          {
00712            viewport a(viewport_[active_viewport_]);
00713 
00714            port*            active_port = viewport_[active_viewport_];
00715            repaint_handler* rh          = active_port->handler_;
00716                 
00717            (*rh)(&a, repaint_handler::activate);
00718         
00719          }
00720         
00721          return;
00722         
00723        }
00724      }
00725    }
00726 
00727    void repaint_ports()
00728    {
00729      int i;
00730 
00731      int resize_cmd = repaint_handler::resize;
00732 
00733      for(i=0; i < viewports_; ++i)
00734      {
00735        if( i != active_viewport_ )
00736        {
00737          port* p = viewport_[i];
00738         
00739          viewport v(p);
00740         
00741          (*p->handler_)(&v, resize_cmd);
00742         
00743        }
00744      }
00745 
00746      if(i)
00747      {
00748        port* a = viewport_[active_viewport_];
00749 
00750        viewport v(a);
00751 
00752        (*a->handler_)(&v, resize_cmd);
00753 
00754      }
00755 
00756    }
00757 
00758    void activate(viewport* p)
00759    {
00760       if(viewports_ && active_viewport_ >= 0)
00761       {
00762         port* a = viewport_[active_viewport_];
00763 
00764         viewport v(a);
00765 
00766         (*a->handler_)(&v, repaint_handler::deactivate);
00767       }
00768 
00769       int i;
00770 
00771       for(i=0; i < viewports_; ++i)
00772       {
00773         if(viewport_[i] == p->port_)
00774         {
00775           active_viewport_ = i;
00776           (*p->port_->handler_)(p, repaint_handler::activate);
00777           break;
00778         }
00779       }
00780 
00781    }
00782 
00783 
00784 
00785 
00786    void resize_viewports(
00787                          row_col const &old_size,
00788                          row_col const &new_size
00789                         )
00790    {
00791      long scaled_row = new_size.row_ * WScale;
00792      long scaled_col = new_size.col_ * WScale;
00793 
00794 
00795      for(int i = 0; i < viewports_; ++i)
00796      {
00797        port* p = viewport_[i];
00798         
00799        long tmp;
00800 
00801        // Before rescaling, see if this window was aligned with the right side
00802        // of the window.  If it was, make sure it still is after the scaling.
00803        // Same goes for the bottom.
00804 
00805        int  right_side     = (p->scaled_origin_.col_ + p->scaled_size_.col_) / WScale;
00806        bool right_aligned  = right_side >= old_size.col_;
00807 
00808        int  bottom_side    = (p->scaled_origin_.row_ + p->scaled_size_.row_) / WScale;
00809        bool bottom_aligned = bottom_side >= old_size.row_;
00810 
00811 
00812        // handle origin
00813 
00814        tmp   = p->scaled_origin_.row_;
00815        tmp  *= new_size.row_;
00816        tmp  /= old_size.row_;
00817 
00818        if(tmp > scaled_row)
00819        {
00820          tmp = scaled_row;
00821        }
00822 
00823        p->scaled_origin_.row_ = tmp;
00824 
00825        tmp   = p->scaled_origin_.col_;
00826        tmp  *= new_size.col_;
00827        tmp  /= old_size.col_;
00828 
00829        if(tmp > scaled_col)
00830        {
00831          tmp = scaled_col;
00832        }
00833        p->scaled_origin_.col_ = tmp;
00834 
00835 
00836        // handle size
00837 
00838        tmp   = p->scaled_size_.row_;
00839        tmp  *= new_size.row_;
00840        tmp  /= old_size.row_;
00841 
00842        if(tmp + p->scaled_origin_.row_ > scaled_row)
00843        {
00844          tmp = scaled_row - p->scaled_origin_.row_;
00845        }
00846        p->scaled_size_.row_ = tmp;
00847 
00848        tmp   = p->scaled_size_.col_;
00849        tmp  *= new_size.col_;
00850        tmp  /= old_size.col_;
00851 
00852        if(tmp + p->scaled_origin_.col_ > scaled_col)
00853        {
00854          tmp = scaled_col - p->scaled_origin_.col_;
00855        }
00856        p->scaled_size_.col_ = tmp;
00857 
00858 
00859        // handle curpos
00860        tmp   = p->curpos_.row_ * WScale;
00861        tmp  *= new_size.row_;
00862        tmp  /= old_size.row_;
00863        p->curpos_.row_ = tmp / WScale;
00864 
00865        tmp   = p->curpos_.col_ * WScale;
00866        tmp  *= new_size.col_;
00867        tmp  /= old_size.col_;
00868        p->curpos_.col_ = tmp / WScale;
00869         
00870 
00871        // handle right and bottom aligned viewports
00872 
00873        if(p->position_ == viewport::floating)
00874        {
00875          //
00876          // if we are dealing with a floating window, don't force it to align up
00877          // to the right or bottom -- or to tile nicely with is friends
00878          //
00879         
00880          continue;
00881         
00882        }
00883 
00884        if(right_aligned)
00885        {
00886          int origin_col = p->scaled_origin_.col_ / WScale;
00887         
00888          int real_width = new_size.col_ - origin_col;
00889         
00890          p->scaled_size_.col_ = real_width * WScale;
00891        }
00892                                 
00893        if(bottom_aligned)
00894        {
00895          int origin_row = p->scaled_origin_.row_ / WScale;
00896         
00897          int real_height = new_size.row_ - origin_row;
00898         
00899          p->scaled_size_.row_ = real_height * WScale;
00900         
00901        }
00902 
00903      }
00904 
00905    }
00906 
00907 };
00908 
00909 static bool key_map_initialized = false;
00910 
00911 
00912 std::map<int,int> CursorWindow::func;
00913 
00914 string CursorWindow::word;
00915 
00916 CursorWindow::
00917 CursorWindow()
00918 {
00919   window_ = new CursorWindow::Window;
00920 
00921   if(!key_map_initialized)
00922   {
00923     key_map_initialized = true;
00924 
00925     func[key_up   ] = func_up;
00926     func[key_down ] = func_down;
00927     func[key_left ] = func_left;
00928     func[key_right] = func_right;
00929     func[key_dc   ] = func_dc;      // delete character
00930     func[key_bs   ] = func_dc_prev; // delete previous character (1 to the left)
00931     #ifdef _MSC_VER
00932     func[0x08     ] = func_dc_prev; // ^H too on dos delete previous character (1 to the left)
00933 
00934     #endif
00935 
00936 
00937     func[key_home ] = func_home;
00938     func[0x01     ] = func_home;    // ^A
00939     func[key_end  ] = func_end;
00940     func[0x05     ] = func_end;     // ^E
00941     func[key_prior] = func_prior;
00942     func[0x0f     ] = func_prior;   // ^O
00943     func[key_next ] = func_next;
00944     func[0x04     ] = func_next;    // ^D
00945     func[0x09     ] = func_tab;
00946     func[key_btab ] = func_btab;
00947     func[0x14     ] = func_top;     // ^T
00948     func[0x02     ] = func_bottom;  // ^B
00949     func[0x1b     ] = func_esc;
00950     func[0x03     ] = func_esc;     // ^C
00951     func['\r'     ] = func_enter;   // Enter Key
00952     func[0x0b     ] = func_clreol;  // ^K
00953     func[0x17     ] = func_nextwd;  // ^W
00954     func[0x11     ] = func_prevwd;  // ^Q
00955     func[0x15     ] = func_upword;  // ^U
00956     func[0x0c     ] = func_dnword;  // ^L
00957     func[0x1f     ] = func_undo;    // ^_  (ctrl shift underscore OR ^/)
00958     func[0x12     ] = func_repl;    // ^R
00959     func[0x19     ] = func_replnext;// ^Y
00960     func[0x13     ] = func_find;    // ^S
00961     func[0x0e     ] = func_findnxt; // ^N
00962     func[0x10     ] = func_findprv; // ^P
00963     func[key_f1   ] = func_help;    // F1
00964     func[key_f3   ] = func_db;      // F3  cut block to paste buffer
00965     func[key_f5   ] = func_mark;    // F5
00966     func[key_f6   ] = func_clreol;  // F6
00967     func[key_f7   ] = func_paste;   // F7  insert the paste buffer above current line
00968     func[key_ic   ] = func_paste;   // Insert key has no other reasonable use
00969                                     // since most keyboard input is in the insert
00970                                     // mode by default
00971     func[key_f8   ] = func_dl;      // F8 (delete line)
00972     func[key_f11  ] = func_cb;      // F11 copy block to paste buffer
00973     func[0x07     ] = func_goto;    // ^G -- goto line
00974     func[0x1c     ] = func_matching;// ^\  (ctrl backslash)
00975     func[0x03     ] = func_dw;      // delete word
00976     func[key_f4   ] = func_join;    // F4  join lines
00977     func[key_f9   ] = func_insline; // F9  insert blank line
00978     func[0x1a     ] = func_to_edit; // ^Z  convert view session to edit
00979     func[key_f12  ] = func_switch;  // F12 switch to alternate source file
00980 
00981     int i;
00982 
00983     for(i=' '; i < 0x7f; ++i)
00984       func[i] = func_data;
00985 
00986     for(i='a'; i <= 'z'; ++i)
00987       word += char(i);
00988 
00989     for(i='A'; i <= 'Z'; ++i)
00990       word += char(i);
00991 
00992     word += '_';
00993     word += '$';
00994 
00995 
00996   }
00997 
00998 }
00999 
01000 string CursorWindow::func_name(int fun)
01001 {
01002   switch(fun)
01003   {
01004     case func_up:      return "CUR_UP_1_LN";
01005     case func_down:    return "CUR_DN_1_LN";
01006     case func_left:    return "CUR_LF_1_CH";
01007     case func_right:   return "CUR_RT_1_CH";
01008     case func_ic:      return "~INSMODE";   // toggle insert mode -- not implemented
01009     case func_dc:      return "DEL_1_CH";
01010     case func_home:    return "HOME_LINE";
01011     case func_end:     return "END_LINE";
01012     case func_prior:   return "PREV_PAGE";
01013     case func_next:    return "NEXT_PAGE";
01014     case func_dc_prev: return "DEL_PRV_CH";
01015     case func_tab:     return "TAB_RIGHT";
01016     case func_btab:    return "TAB_LEFT";
01017     case func_top:     return "TOP";
01018     case func_bottom:  return "BOTTOM";
01019     case func_esc:     return "ESCAPE";
01020     case func_enter:   return "ENTER";
01021     case func_data:    return "DATA";
01022     case func_clreol:  return "CLR_2_EOL";
01023     case func_nextwd:  return "NXT_WD";
01024     case func_prevwd:  return "PRV_WD";
01025     case func_upword:  return "UPCASE_WD";
01026     case func_dnword:  return "DNCASE_WD";
01027     case func_undo:    return "UNDO";
01028     case func_find:    return "FIND";
01029     case func_findnxt: return "FIND_NEXT";
01030     case func_findprv: return "FIND_PREV";
01031     case func_repl:    return "REPLACE";
01032     case func_replnext:return "REPLACE_NXT";
01033     case func_help:    return "HELP";
01034     case func_mark:    return "MARK (block)";
01035     case func_dl:      return "DEL_1_LINE";
01036     case func_dw:      return "DEL_WORD";
01037     case func_db:      return "DEL_BLOCK";
01038     case func_cb:      return "COPY_BLOCK";
01039     case func_paste:   return "PASTE (block)";
01040     case func_join:    return "JOIN (lines)";
01041     case func_insline: return "INSERT_BLANK_LINE";
01042     case func_to_edit: return "CONV VIEW TO EDIT";
01043     case func_reread:  return "RE_READ_FILE"; 
01044   };
01045 
01046   return "USER_FUNC";
01047 
01048 }
01049 
01050 
01051 
01052 
01053 CursorWindow::
01054 ~CursorWindow()
01055 {
01056   if(window_->open_)
01057     close();
01058 
01059   delete window_;
01060 
01061   window_ = 0;
01062 
01063 }
01064 
01065 void
01066 CursorWindow::
01067 close()
01068 {
01069   closeCursesTerminal();
01070   window_->open_ = false;
01071 }
01072 
01073 
01074 bool
01075 CursorWindow::
01076 open()
01077 {
01078   if(!window_->open_)
01079   {
01080     openCursesTerminal();
01081 
01082     window_->open_ = true;
01083 
01084     getCursesScreenSize(&window_->size_.row_,&window_->size_.col_);
01085 
01086   }
01087 
01088   return false;
01089 }
01090 
01091 char const *
01092 CursorWindow::
01093 error() const
01094 {
01095   return window_->error_;
01096 }
01097 
01098 void
01099 CursorWindow::
01100 set_error(char const *s)
01101 {
01102   window_->error_ = s;
01103 }
01104 
01105 
01106 void
01107 CursorWindow::
01108 needs_resized()
01109 {
01110    window_->needs_resized_ = true;
01111    resize_requested = 1;
01112 }
01113 
01114 
01115 void
01116 CursorWindow::
01117 refresh()
01118 {
01119   if(window_->needs_resized_)
01120   {
01121     window_->needs_resized_ = 0;
01122     resize_requested        = 0;
01123 
01124     if(window_->resize_handler_)
01125       (*window_->resize_handler_)(this);
01126 
01127     window_->repaint_ports();
01128 
01129   }
01130   refreshCursesWindow();
01131 }
01132 
01133 
01134 
01135 CursorWindow::input_event
01136 CursorWindow::
01137 read_input()
01138 {
01139 
01140   int key;
01141 
01142   //
01143   // read events and handle resizes by either calling the window's resize
01144   // handler or by returning a resize event.
01145   //
01146 
01147   refresh();
01148 
01149 
01150   for(;;)
01151   {
01152 
01153     key = CursesInterface::read_mapped_key(*this);
01154 
01155     if(key == CursesInterface::ResizeEvent)
01156     {
01157       winch_occurred   = 0;  // officially handling winch
01158       resize_requested = 0;
01159 
01160       row_col old_size = window_->size_;
01161 
01162       getCursesScreenSize(&window_->size_.row_,
01163                           &window_->size_.col_
01164                          );
01165                         
01166       row_col new_size = window_->size_;
01167 
01168       if(window_->resize_handler_)
01169       {
01170         (*window_->resize_handler_)(this);
01171 
01172         window_->repaint_ports();
01173 
01174         winch_occurred   = 0;  // officially handling winch
01175         resize_requested = 0;
01176 
01177         continue;
01178       }
01179 
01180       // no resize handler is specified -- so use a default
01181       // behavior -- but what?
01182 
01183       // The default resize handler re-scales the viewports
01184       // in the window to fit in the available space.
01185         
01186       window_->resize_viewports(old_size, new_size);
01187 
01188       window_->repaint_ports();
01189 
01190       winch_occurred   = 0;  // officially handling winch
01191       resize_requested = 0;
01192 
01193       return input_event(input_event::ResizeKey, 0);
01194 
01195     }
01196     else
01197     if(key == CursesInterface::MouseEvent)
01198     {
01199 #ifdef _MSC_VER
01200       CursesInterface::mouse_info loc = CursesInterface::read_mouse_info();
01201 
01202       int buttonCode = '@' + loc.state_;
01203 
01204 #if 0
01205       row_col org = origin();
01206 #else
01207       row_col org(0,0); // stubbed out till implemented
01208 #endif
01209 
01210       return input_event(input_event::MouseEvent, buttonCode, loc.row_ - org.row_, loc.col_ - org.col_);
01211 #endif
01212     }
01213     else
01214       break;
01215   }
01216 
01217 
01218   // at this point, we know we have a normal key event
01219 
01220 
01221   std::map<int,int>::iterator key_table_loc = func.find(key);
01222 
01223   if(key_table_loc == func.end() ||
01224      key_table_loc->second == CursorWindow::func_data
01225     )
01226   {
01227     return input_event(input_event::DataKey, key);  // its not a function key
01228   }
01229 
01230 
01231   return input_event(input_event::FunctionKey, key );
01232 
01233 }
01234 
01235 row_col
01236 CursorWindow::
01237 curpos() const
01238 {
01239    row_col rc;
01240 
01241    getCursesCursor(&rc.row_, &rc.col_);
01242 
01243    return rc;
01244 }
01245 
01246 void
01247 CursorWindow::
01248 set_curpos(row_col rc)
01249 {
01250   moveCursesCursor(rc.row_, rc.col_);
01251 }
01252 
01253 row_col
01254 CursorWindow::
01255 size() const
01256 {
01257   return window_->size_;
01258 }
01259 
01260 void
01261 CursorWindow::
01262 beep()
01263 {
01264   beepCursesTerminal();
01265 }
01266 
01267 void
01268 CursorWindow::
01269 write(char const *s, size_t length)
01270       //
01273 {
01274   row_col where = curpos();
01275 
01276   if(where.row_ < 0                    ||
01277      where.row_ >= window_->size_.row_ ||
01278      where.col_ >= window_->size_.col_
01279     )
01280     return;  // off screen, top, bottom, or right
01281 
01282   if(where.col_ < 0)
01283   {
01284     int t = -where.col_ ;
01285 
01286     if(t >= (int)length)
01287     {
01288       return ;  // off screen to the left
01289     }
01290 
01291     // overlaps the screen
01292 
01293     s += t;  // skip parts of string to the left of the screen
01294 
01295     length -= t;  // reduce the length of the drawn string to be the visible
01296                   // parts
01297 
01298     where.col_ = 0; // and of course start at the left edge
01299 
01300   }
01301 
01302   paintCharString(s, length,  window_->current_attribute_, where.row_, where.col_);
01303 
01304 }
01305 
01306 void
01307 CursorWindow::
01308 write(long c, int length)
01309       //
01312 {
01313   row_col where = curpos();
01314 
01315   if(where.row_ < 0                    ||
01316      where.row_ >= window_->size_.row_ ||
01317      where.col_ >= window_->size_.col_
01318     )
01319     return;  // off screen, top, bottom, or right
01320 
01321   if(where.col_ < 0)
01322   {
01323     int t = -where.col_ ;
01324 
01325     if(t >= (int)length)
01326     {
01327       return ;  // off screen to the left
01328     }
01329 
01330     // overlaps the screen
01331 
01332     length -= t;  // reduce the length of the drawn string to be the visible
01333                   // parts
01334 
01335     where.col_ = 0; // and of course start at the left edge
01336 
01337   }
01338 
01339   paintCharString(c, length,  window_->current_attribute_, where.row_, where.col_);
01340 
01341 }
01342 
01343 
01344 void
01345 CursorWindow::
01346 fill_to_eol(char c)
01347 {
01348   int remaining_length = size().col_ - curpos().col_;
01349 
01350   write(c, remaining_length);
01351 
01352 }
01353 
01354 void
01355 CursorWindow::
01356 fill_to_eos(char c)
01357 {
01358    int i;
01359 
01360    row_col where = curpos();
01361 
01362    int rows = size().row_;
01363 
01364    fill_to_eol(c);
01365 
01366    for(i = where.row_+1; i < rows; ++i)
01367    {
01368      set_curpos(i, 0);
01369      fill_to_eol(c);
01370    }
01371 
01372 
01373 }
01374 
01375 void
01376 CursorWindow::
01377 box(int width, int height, bool filled, char const *string, unsigned length)
01378 {
01379    using namespace CursesInterface;
01380 
01381    row_col where = curpos();
01382    row_col dims  = size();
01383 
01384    int row = where.row_;
01385    int col = where.col_;
01386 
01387    if(width + where.col_ >= dims.col_)
01388    {
01389      // truncate to end of screen
01390 
01391      width = dims.col_ - where.col_;
01392 
01393      if(width < 1)
01394        return;  
01395 
01396    }
01397 
01398    int count=0;
01399 
01400    long horz = line_chars[HL_MIDDLE];
01401    long fill = line_chars[HL_MIDDLE];
01402    long tl   = line_chars[UL_CORNER];
01403    long bl   = line_chars[LL_CORNER];
01404    long tr   = line_chars[UR_CORNER];
01405    long br   = line_chars[LR_CORNER];
01406    long bar  = line_chars[VL_MIDDLE];
01407    
01408    while(row < dims.row_ && count < height)
01409    {
01410      // paint each row but don't run off the screen
01411 
01412      row_col here(row,col);
01413 
01414      if(filled)
01415      {
01416        set_curpos( here );
01417 
01418        if(count == height-1)
01419          fill = horz;
01420 
01421        for(int i = col; i < col + width; ++i)
01422          write(fill, 1);
01423 
01424        fill = ' ';
01425      }
01426 
01427 
01428      if(count == 0)
01429      {
01430        set_curpos( here );
01431        write(tl, 1);
01432        set_curpos( row_col(row, col + width-1));
01433        write(tr, 1);
01434      }
01435      else
01436      if(count == height-1)
01437      {
01438        set_curpos( here );
01439        write(bl, 1);
01440        set_curpos( row_col(row, col + width-1));
01441        write(br, 1);
01442      }
01443      else
01444      {
01445        set_curpos( here );
01446        write(bar, 1);
01447        set_curpos( row_col(row, col + width-1));
01448        write(bar, 1);
01449      }
01450 
01451      ++row;
01452      ++count;
01453    }
01454 
01455 
01456    if(string)
01457    {
01458      set_curpos( row_col( where.row_, where.col_ + 2 ) );
01459 
01460      write(string, length);
01461      
01462    }
01463 
01464 
01465 }
01466 
01467 int
01468 CursorWindow::
01469 text_attribute() const
01470 {
01471   return window_->current_attribute_;
01472 }
01473 
01474 void
01475 CursorWindow::
01476 set_text_attribute(int a)
01477 {
01478   window_->current_attribute_ = a;
01479 }
01480 
01481 
01482 string
01483 CursorWindow::
01484 key_name(int curses_key)
01485 {
01486   switch(curses_key)
01487   {
01488     case CursorWindow::key_up:      return "key_up";
01489     case CursorWindow::key_down:    return "key_down";
01490     case CursorWindow::key_left:    return "key_left";
01491     case CursorWindow::key_right:   return "key_right";
01492     case CursorWindow::key_sleft:   return "key_sleft";
01493     case CursorWindow::key_sright:  return "key_sright";
01494     case CursorWindow::key_prior:   return "key_prior";
01495     case CursorWindow::key_next:    return "key_next";
01496     case CursorWindow::key_ic:      return "key_ic";
01497     case CursorWindow::key_dc:      return "key_dc";
01498     case CursorWindow::key_bs:      return "key_bs";
01499     case CursorWindow::key_home:    return "key_home";
01500     case CursorWindow::key_end:     return "key_end";
01501     case CursorWindow::key_btab:    return "key_btab";
01502 
01503     case CursorWindow::key_f1:   return "key_f1";
01504     case CursorWindow::key_f2:   return "key_f2";
01505     case CursorWindow::key_f3:   return "key_f3";
01506     case CursorWindow::key_f4:   return "key_f4";
01507     case CursorWindow::key_f5:   return "key_f5";
01508     case CursorWindow::key_f6:   return "key_f6";
01509     case CursorWindow::key_f7:   return "key_f7";
01510     case CursorWindow::key_f8:   return "key_f8";
01511     case CursorWindow::key_f9:   return "key_f9";
01512     case CursorWindow::key_f10:   return "key_f10";
01513     case CursorWindow::key_f11:   return "key_f11";
01514     case CursorWindow::key_f12:   return "key_f12";
01515     case 0x1b:                    return "key_esc";
01516     case 0x1c:                    return "^\\";
01517     case 0x1f:                    return "^/ or ^_";
01518   }
01519 
01520   char buffer[40];
01521 
01522   if(curses_key >= ' ' && curses_key <= 0x7e)
01523     sprintf(buffer, "'%c'", curses_key);
01524   else
01525     sprintf(buffer, "^%c", '@' + curses_key);
01526 
01527 
01528   return buffer;
01529 
01530 }
01531 
01532 void
01533 CursorWindow::set_resize_handler( CursorWindow::resize_handler *h )
01534 {
01535   window_->resize_handler_ = h;
01536 }
01537 
01538 void
01539 viewport::
01540 beep()
01541 {
01542   window()->beep();
01543 }
01544 
01545 
01546 viewport::
01547 viewport(CursorWindow* w, repaint_handler* r, viewport::position p)
01548 {
01549   port_ = w->window_->new_port(w,r,p);
01550   owned_ = true;
01551 }
01552 
01553 viewport::
01554 viewport(port* p)
01555 {
01556   port_ = p;
01557   owned_ = false;
01558 }
01559 
01560 
01561 viewport::
01562 ~viewport()
01563 {
01564   if(owned_)
01565     port_->window_->window_->delete_port(port_);
01566 
01567   port_ = 0;
01568 }
01569 
01570 
01571 void
01572 viewport::
01573 write(char const *s, size_t length)
01574       //
01577 {
01578   row_col where = curpos();
01579   row_col vsize  = size();
01580 
01581   if(where.row_ < 0                  ||
01582      where.row_ >= port_->size().row_ ||
01583      where.col_ >= port_->size().col_
01584     )
01585     return;  // off screen, top, bottom, or right
01586 
01587   if(where.col_ < 0)
01588   {
01589     int t = -where.col_ ;
01590 
01591     if(t >= (int)length)
01592     {
01593       return ;  // off screen to the left
01594     }
01595 
01596     // overlaps the screen
01597 
01598     s += t;  // skip parts of string to the left of the screen
01599 
01600     length -= t;  // reduce the length of the drawn string to be the visible
01601                   // parts
01602 
01603     where.col_ = 0; // and of course start at the left edge
01604 
01605   }
01606 
01607   port_->window_->set_text_attribute(port_->current_attribute_);
01608 
01609 
01610   port_->window_->set_curpos(where.row_ + port_->origin().row_,
01611                              where.col_ + port_->origin().col_);
01612 
01613 
01614   if(where.col_ + length > size_t(vsize.col_) )
01615   {
01616       length = vsize.col_ - where.col_;
01617   }
01618 
01619   port_->window_->write(s, length);
01620 
01621   port_->curpos_.col_ += length;
01622 
01623 }
01624 
01625 void
01626 viewport::
01627 write(long c, int length)
01628       //
01631 {
01632   row_col where = curpos();
01633 
01634   if(where.row_ < 0                  ||
01635      where.row_ >= port_->size().row_ ||
01636      where.col_ >= port_->size().col_
01637     )
01638     return;  // off screen, top, bottom, or right
01639 
01640   if(where.col_ < 0)
01641   {
01642     int t = -where.col_ ;
01643 
01644     if(t >= (int)length)
01645     {
01646       return ;  // off screen to the left
01647     }
01648 
01649     // overlaps the screen
01650 
01651     length -= t;  // reduce the length of the drawn string to be the visible
01652                   // parts
01653 
01654     where.col_ = 0; // and of course start at the left edge
01655 
01656   }
01657 
01658   port_->window_->set_text_attribute(port_->current_attribute_);
01659 
01660   port_->window_->set_curpos(where.row_ + port_->origin().row_,
01661                              where.col_ + port_->origin().col_);
01662 
01663   port_->window_->write(c, length);
01664 
01665   port_->curpos_.col_ += length;
01666 
01667 }
01668 
01669 
01670 void
01671 viewport::
01672 fill_to_eol(char c)
01673 {
01674   int remaining_length = size().col_ - curpos().col_;
01675 
01676   write(c, remaining_length);
01677 
01678 }
01679 
01680 void
01681 viewport::
01682 fill_to_eos(char c)
01683 {
01684   int i;
01685 
01686   fill_to_eol(c);
01687 
01688   for(i=curpos().row_ + 1; i < size().row_; ++i)
01689   {
01690     set_curpos(i, 0);
01691     fill_to_eol(c);
01692   }
01693 
01694 
01695 }
01696 
01697 void
01698 viewport::
01699 box(int width, int height, bool filled, char const *string, unsigned length)
01700 {
01701    row_col where = curpos();
01702    row_col dims  = size();
01703 
01704    int row = where.row_;
01705    int col = where.col_;
01706 
01707    if(width + where.col_ >= dims.col_)
01708    {
01709      // truncate to end of screen
01710 
01711      width = dims.col_ - where.col_;
01712 
01713      if(width < 1)
01714        return;  
01715 
01716    }
01717 
01718    int count=0;
01719 
01720 
01721    long horz = line_chars[HL_MIDDLE];
01722    long fill = line_chars[HL_MIDDLE];
01723    long tl   = line_chars[UL_CORNER];
01724    long bl   = line_chars[LL_CORNER];
01725    long tr   = line_chars[UR_CORNER];
01726    long br   = line_chars[LR_CORNER];
01727    long bar  = line_chars[VL_MIDDLE];
01728    
01729    while(row < dims.row_ && count < height)
01730    {
01731      // paint each row but don't run off the screen
01732 
01733      row_col here(row,col);
01734 
01735      if(filled)
01736      {
01737        set_curpos( here );
01738 
01739        if(count == height-1)
01740          fill = horz;
01741 
01742        for(int i = col; i < col + width; ++i)
01743          write(fill, 1);
01744 
01745        fill = ' ';
01746      }
01747 
01748 
01749      if(count == 0)
01750      {
01751        set_curpos( here );
01752        write(tl, 1);
01753        set_curpos( row_col(row, col + width-1));
01754        write(tr, 1);
01755      }
01756      else
01757      if(count == height-1)
01758      {
01759        set_curpos( here );
01760        write(bl, 1);
01761        set_curpos( row_col(row, col + width-1));
01762        write(br, 1);
01763      }
01764      else
01765      {
01766        set_curpos( here );
01767        write(bar, 1);
01768        set_curpos( row_col(row, col + width-1));
01769        write(bar, 1);
01770      }
01771 
01772      ++row;
01773      ++count;
01774    }
01775 
01776 
01777    if(string)
01778    {
01779      set_curpos( row_col( where.row_, where.col_ + 2 ) );
01780 
01781      write(string, length);
01782      
01783    }
01784 
01785 
01786 }
01787 
01788 
01789 row_col
01790 viewport::
01791 curpos() const
01792 {
01793   return port_->curpos_;
01794 }
01795 
01796 void
01797 viewport::
01798 set_curpos(row_col rc)
01799 {
01800   port_->curpos_ = rc;
01801 
01802   rc.row_ += port_->origin().row_;
01803   rc.col_ += port_->origin().col_;
01804 
01805 
01806   port_->window_->set_curpos(rc);
01807 }
01808 
01809 void
01810 viewport::
01811 restore_curpos()
01812 {
01813   set_curpos(port_->curpos_);
01814 }
01815 
01816 
01817 int
01818 viewport::
01819 text_attribute() const
01820 {
01821   return port_->current_attribute_;
01822 }
01823 
01824 void
01825 viewport::
01826 set_text_attribute(int a)
01827 {
01828   port_->current_attribute_ = a;
01829 }
01830 
01831 std::auto_ptr<viewport>
01832 CursorWindow::new_viewport(repaint_handler* h, viewport::position p)
01833 {
01834   return std::auto_ptr<viewport>(new viewport(this,h,p));
01835 }
01836 
01837 
01838 row_col
01839 viewport::
01840 size() const
01841 {
01842   row_col rv;
01843 
01844   rv.row_ = port_->scaled_size_.row_ / WScale;
01845   rv.col_ = port_->scaled_size_.col_ / WScale;
01846 
01847   return rv;
01848 }
01849 
01850 row_col
01851 viewport::
01852 origin() const
01853 {
01854   row_col rv;
01855 
01856   rv.row_ = port_->scaled_origin_.row_ / WScale;
01857   rv.col_ = port_->scaled_origin_.col_ / WScale;
01858 
01859   return rv;
01860 
01861 }
01862 
01863 
01864 void
01865 viewport::
01866 move( row_col origin,
01867       row_col size
01868     )
01869 {
01870   row_col port_origin;
01871 
01872     port_origin.row_ = port_->scaled_origin_.row_ / WScale;
01873     port_origin.col_ = port_->scaled_origin_.col_ / WScale;
01874 
01875   row_col port_size;
01876 
01877     port_size.row_ = port_->scaled_size_.row_ / WScale;
01878     port_size.col_ = port_->scaled_size_.col_ / WScale;
01879 
01880   if(origin == port_origin && size == port_size)
01881     return;
01882 
01883 
01884   port_->scaled_origin_.row_ = origin.row_ * WScale + WHalfCell;
01885   port_->scaled_origin_.col_ = origin.col_ * WScale + WHalfCell;
01886 
01887   port_->scaled_size_.row_ = size.row_ * WScale + WHalfCell;
01888   port_->scaled_size_.col_ = size.col_ * WScale + WHalfCell;
01889 
01890   port_->window_->needs_resized();
01891 
01892 }
01893 
01894 void
01895 viewport::
01896 activate()
01897 {
01898   port_->window_->window_->activate(this);
01899 }
01900 
01901 void
01902 viewport::
01903 refresh()
01904 {
01905   port_->window_->refresh();
01906 }
01907 
01908 CursorWindow*
01909 viewport::
01910 window()
01911 {
01912   return port_->window_;
01913 }
01914 
01915 
01916 
01917 CursorWindow::resize_handler::
01918 ~resize_handler()
01919 {
01920 }
01921 
01922 #undef repaint_handler
01923 
01924 viewport::repaint_handler::
01925 ~repaint_handler()
01926 {
01927 }
01928 
01929 void
01930 CursorWindow::Dialog::Ok::
01931 handle_key(viewport *v, int key)
01932 {
01933   v->beep();
01934 }
01935 
01936 
01937 class CursorWindow::Dialog::Impl
01938   //
01941 {
01942 public:
01943 
01944   typedef CursorWindow::Dialog::element_list_t  element_list_t;
01945   typedef element_list_t::iterator              iterator;
01946 
01947   string title_;
01949 
01950   element_list_t  elements_;
01952 
01953   std::string curdir_;
01956 
01957   std::string first_input_field_;
01960 
01961   iterator find_element(string const &name);
01963 
01964 
01965   struct visible_page
01966     //
01969   {
01970     iterator page_top_;      
01971     iterator page_bottom_;   
01972     iterator current_line_;  
01973     int      height_;        
01974     iterator last_line_;     
01975 
01976     int page_offset(iterator line)
01977       //
01978       // offset from top of displayed page to the current line
01979       // (expressed in input fields, starting with 0)
01980       //
01981     {
01982       iterator scan = page_top_;
01983 
01984       int rv = 0;
01985 
01986       while(scan != line && scan != last_line_)
01987       {
01988         ++rv;
01989         ++scan;
01990       }
01991 
01992 
01993       return rv;
01994     }
01995 
01996     int page_offset()
01997     {
01998       return page_offset(current_line_);
01999     }
02000 
02001 
02002   };
02003 
02004   visible_page page_;    
02005 
02006   int desired_width_;    
02007   int desired_height_;   
02008   int actual_width_;     
02009   int actual_height_;    
02010   int gutter_width_;     
02011   int first_input_row_;  
02012   int title_row_;        
02013   int title_col_;        
02014   int label_indent_;     
02015 
02016   int desired_label_width_;
02017   int desired_input_width_;
02018   int desired_input_column_;     
02019   int actual_input_column_;
02020 
02021   void draw_current_input_area(viewport *v, bool highlighted)
02022   {
02023 
02024     int row = page_.page_offset();
02025 
02026     v->set_curpos(row + first_input_row_,
02027                  actual_input_column_
02028                 );
02029                 
02030     CursorWindow::Dialog::Element* e = *page_.current_line_;
02031 
02032     e->draw_input_area(v, highlighted);
02033 
02034   }
02035 
02036   void recompute_page_info(viewport* v)
02037   {
02038     row_col available = v->window()->size();
02039 
02040     actual_input_column_ = desired_input_column_;
02041 
02042     int trim = desired_width_ - available.col_;
02043 
02044     if(trim >= 1)
02045     {
02046       // dialog is too wide -- reduce both labels and input areas
02047 
02048       actual_input_column_ -= trim;
02049 
02050       actual_width_ = available.col_;
02051 
02052     }
02053     else
02054       actual_width_ = desired_width_;
02055 
02056     if(desired_height_ > available.row_)
02057       actual_height_ = available.row_;
02058     else
02059       actual_height_ = desired_height_;
02060 
02061     int input_row;
02062 
02063     int max_input_rows = actual_height_ - first_input_row_;
02064 
02065     for(input_row = 0, page_.page_bottom_ = page_.page_top_;
02066         input_row < max_input_rows && page_.page_bottom_ != page_.last_line_;
02067         ++input_row, ++page_.page_bottom_
02068        );
02069 
02070 
02071     page_.height_ = input_row;
02072 
02073 
02074     row_col upper_left( (available.row_ - actual_height_)/2,
02075                                       (available.col_ - actual_width_)/2
02076                                     );
02077 
02078 
02079     v->move(upper_left, row_col(actual_height_,actual_width_));
02080 
02081   }
02082 
02083 
02084 };
02085 
02086 
02087 void
02088 CursorWindow::Dialog::
02089 set_first_input_field(std::string const &field_name)
02090 {
02091   impl_->first_input_field_ = field_name;
02092 }
02093 
02094 
02095 void dumpimpl(CursorWindow::Dialog::Impl *impl_)
02096 {
02097   //
02098   // This is a debug helper only, it is pointless to print the stuff for
02099   // any other reason.
02100   //
02101 
02102   string beg="\r";
02103 
02104   string label = (*impl_->page_.page_bottom_)->label_;
02105 
02106 
02107   std::cout << "desired width       = " << impl_->desired_width_        << beg << std::endl;
02108   std::cout << "desired height      = " << impl_->desired_height_       << beg << std::endl;
02109   std::cout << "actual_width        = " << impl_->actual_width_         << beg << std::endl;
02110   std::cout << "actual_height       = " << impl_->actual_height_        << beg << std::endl;
02111   std::cout << "gutterwidth         = " << impl_->gutter_width_         << beg << std::endl;
02112   std::cout << "first_input_row     = " << impl_->first_input_row_      << beg << std::endl;
02113   std::cout << "title row           = " << impl_->title_row_            << beg << std::endl;
02114   std::cout << "title col           = " << impl_->title_col_            << beg << std::endl;
02115   std::cout << "label indent        = " << impl_->label_indent_         << beg << std::endl;
02116   std::cout << "desired label width = " << impl_->desired_label_width_  << beg << std::endl;
02117   std::cout << "desired input widht = " << impl_->desired_input_width_  << beg << std::endl;
02118   std::cout << "desired input col   = " << impl_->desired_input_column_ << beg << std::endl;
02119   std::cout << "actual input col    = " << impl_->actual_input_column_  << beg << std::endl;
02120   std::cout << "page_bottom_        = " << label                        << beg << std::endl;
02121   std::cout << std::flush;
02122 }
02123 
02124 CursorWindow::Dialog::
02125 Dialog(string title)
02126 {
02127   impl_ = new CursorWindow::Dialog::Impl;
02128   impl_->title_ = title;
02129 
02130   impl_->gutter_width_    = 2;  // ": "
02131   impl_->first_input_row_ = 1;  // first row under title
02132   impl_->title_row_       = 0;  // top of dialog
02133   impl_->title_col_       = 0;  // offset from left side to title
02134   impl_->label_indent_    = 2;  // from left side of dialog to labels
02135 
02136   // safe initializations
02137 
02138   impl_->desired_width_        = 0;
02139   impl_->desired_height_       = 0;
02140   impl_->actual_width_         = 0;
02141   impl_->actual_height_        = 0;
02142   impl_->desired_input_column_ = 0;
02143   impl_->actual_input_column_  = 0;
02144   impl_->desired_label_width_  = 0;
02145   impl_->desired_input_width_  = 0;
02146 
02147   impl_->page_.page_top_       = end();
02148   impl_->page_.page_bottom_    = end();
02149   impl_->page_.current_line_   = end();
02150   impl_->page_.height_         = 0;
02151   impl_->page_.last_line_      = end();
02152 
02153 
02154 }
02155 
02156 CursorWindow::Dialog::
02157 ~Dialog()
02158 {
02159   for(iterator f = begin(), l = end(); f != l; ++f)
02160   {
02161     delete *f;
02162   }
02163 
02164   delete impl_;
02165 
02166   impl_ = 0;
02167 }
02168 
02169 CursorWindow::Dialog&
02170 CursorWindow::Dialog::
02171 operator+= (CursorWindow::Dialog::Element *e)
02172   //
02174   //
02175 {
02176   e->dialog_ = this;
02177 
02178   impl_->elements_.push_back(e);
02179 
02180   return *this;
02181 
02182 }
02183 
02184 std::string const &
02185 CursorWindow::Dialog::
02186 completion_directory() const
02187 {
02188   return impl_->curdir_;
02189 }
02190 
02191 void
02192 CursorWindow::Dialog::
02193 set_completion_directory(std::string const &s)
02194 {
02195   impl_->curdir_ = s;
02196 }
02197 
02198 
02199 
02200 CursorWindow::Dialog::iterator
02201 CursorWindow::Dialog::
02202 begin()
02203 {
02204   return impl_->elements_.begin();
02205 }
02206 
02207 
02208 CursorWindow::Dialog::iterator
02209 CursorWindow::Dialog::
02210 end()
02211 {
02212   return impl_->elements_.end();
02213 }
02214 
02215 void
02216 CursorWindow::Dialog::Ok::
02217 draw_input_area(viewport *v, bool highlighted)
02218 {
02219   v->set_text_attribute( highlighted ? CursorWindow::normal
02220                                      : CursorWindow::reversed
02221                        );
02222                 
02223   row_col pos = v->curpos();
02224                 
02225   v->write("[OK]");
02226 
02227   v->set_curpos(pos.row_, pos.col_ + 1);
02228 
02229 }
02230 
02231 
02232 
02233 
02234 bool
02235 CursorWindow::Dialog::String::
02236 compute_actual_width(viewport *v)
02237 {
02238   bool rv = false;
02239 
02240   width_ = input_width_;            // actual equals requested
02241 
02242   row_col size = v->size();
02243 
02244   if(input_ + width_ > size.col_)   // if actual won't fit in window, shrink it
02245   {
02246     width_ = size.col_ - input_;    // to the size between actual input column
02247                                     // and the end of the window
02248   }
02249 
02250   if(col_ < 0)
02251     col_ = 0;
02252 
02253   int maxlen = value_.size();
02254 
02255   if(col_ > maxlen )                // col_ is allowed to be exactly maxlen
02256     col_ = maxlen;
02257 
02258   if(col_ - offset_ >= width_ ||    // logical cursor not in the visible area
02259      col_ < offset_
02260     )
02261   {
02262     offset_ = col_ - width_/2;      // center logical cursor pos in the window
02263 
02264     if(offset_ < 0)
02265       offset_ = 0;
02266 
02267     rv = true;                      // offset_ changed
02268   }
02269 
02270 
02271   if(offset_ > maxlen )
02272   {
02273     offset_ = maxlen;
02274 
02275     rv = true;                      // offset_ changed
02276   }
02277 
02278   if(col_ > maxlen)
02279     col_ = maxlen;
02280 
02281 
02282   return rv;
02283 
02284 }
02285 
02286 void
02287 CursorWindow::Dialog::String::
02288 handle_paging(viewport *v)
02289 {
02290   row_col pos = v->curpos();
02291 
02292   if(compute_actual_width(v))
02293   {
02294     v->set_curpos(pos.row_, input_);
02295 
02296     draw_input_area(v,true);
02297 
02298   }
02299 
02300   v->set_curpos(pos.row_, input_ + col_ - offset_);
02301 
02302 
02303 }
02304 
02305 
02306 void
02307 CursorWindow::Dialog::String::
02308 draw_input_area(viewport *v, bool highlighted)
02309 {
02310   // this code assumes that the cursor is correctly positioned at the
02311   // beginning of the input area and the cursor position is captured for
02312   // future use by handle_key
02313 
02314   row_col pos = v->curpos();
02315 
02316   input_ = pos.col_;        // actual_input_col_ from the impl_ in the dialog.
02317 
02318   compute_actual_width(v);  // and offset_
02319 
02320   int drawlen = value_.size();
02321 
02322   drawlen -= offset_;
02323 
02324   if(drawlen > width_)
02325     drawlen = width_;
02326 
02327   v->set_text_attribute( highlighted ? CursorWindow::normal
02328                                      : CursorWindow::reversed
02329                        );
02330 
02331   if(password_)
02332   {
02333     v->write('*', drawlen);
02334   }
02335   else
02336   {
02337     v->write( string(value_.begin() + offset_,
02338                      value_.begin() + offset_ + drawlen
02339                     )
02340             );
02341   }
02342         
02343   if(drawlen < width_)
02344     v->write(' ', width_ - drawlen);
02345 
02346   v->set_curpos(pos.row_, pos.col_ + col_ - offset_);
02347 
02348 }
02349 
02350 void CursorWindow::Dialog::String::
02351 setColumn(int col)
02352 {
02353     col_ = (size_t(col) <= value_.size() ) ? col : 0;
02354 };
02355 
02356 
02357 void
02358 CursorWindow::Dialog::String::
02359 handle_key(viewport *v, int key)
02360 {
02361   //
02362   // Find out where the cursor actually is
02363 
02364   compute_actual_width(v);
02365 
02366   switch(CursorWindow::func[key])
02367   {
02368     case CursorWindow::func_home:
02369       {
02370         col_ = 0;
02371         
02372         handle_paging(v);
02373         
02374       }
02375       break;
02376 
02377     case CursorWindow::func_end:
02378       {
02379         col_ = value_.size();
02380         
02381         handle_paging(v);
02382         
02383       }
02384       break;
02385 
02386     case CursorWindow::func_right:
02387       {
02388         ++col_;
02389         
02390         handle_paging(v);
02391         
02392       }
02393       break;
02394 
02395 
02396     case CursorWindow::func_left:
02397       {
02398         --col_;
02399         
02400         handle_paging(v);
02401         
02402       }
02403       break;
02404 
02405     case CursorWindow::func_dc_prev:
02406 
02407       if(col_ == 0)
02408       {
02409         v->beep();
02410         return;
02411       }
02412 
02413       --col_;
02414       compute_actual_width(v);
02415       {
02416         row_col pos = v->curpos();
02417         
02418         v->set_curpos(pos.row_, input_ + col_ - offset_);
02419         
02420       }
02421 
02422       // drop through
02423 
02424     case CursorWindow::func_dc:
02425       {
02426         int maxlen = value_.size();
02427         
02428         if(col_ < maxlen)
02429         {
02430            value_.erase( value_.begin() + col_ );
02431         
02432            int tail_length = maxlen - col_ - 1;
02433         
02434            string tail_text(value_.begin() + col_,
02435                             value_.begin() + col_ + tail_length
02436                            );
02437                         
02438            tail_text += " ";
02439         
02440            if(password_)
02441            {
02442              v->write('*', tail_text.size()-1 );
02443              v->write(' ');
02444         
02445            }
02446            else
02447              v->write( tail_text );
02448 
02449            handle_paging(v);
02450 
02451         }
02452         else
02453           v->beep();
02454       }
02455       break;
02456 
02457     case CursorWindow::func_clreol:
02458       {
02459         int maxlen = value_.size();
02460         
02461         if(col_ < maxlen)
02462         {
02463            value_.erase( value_.begin() + col_, value_.end() );
02464         
02465            int tail_length = maxlen - col_;
02466         
02467            string tail_text(tail_length, ' ');
02468                         
02469            if(password_)
02470            {
02471              v->write(' ', tail_text.size());
02472         
02473            }
02474            else
02475              v->write( tail_text );
02476 
02477            handle_paging(v);
02478 
02479         }
02480         else
02481           v->beep();
02482       }
02483       break;
02484 
02485     case CursorWindow::func_tab:
02486       {
02487         // perform tab completion
02488         
02489         size_t col = col_;
02490         
02491         if(col > value_.size())
02492           col = value_.size();
02493         
02494         // col is now within the valid range of characters in
02495         // the value string -- or it is at the end
02496 
02497         if(value_[0] == '~')
02498         { // expand tildes
02499 
02500           FileName tmp(value_);
02501 
02502           value_ = tmp.expand_tildes();
02503 
02504           // paint the input area as blank
02505           
02506           row_col pos = v->curpos();
02507           
02508           v->set_curpos(pos.row_, input_);
02509           
02510           v->write(' ', width_);
02511 
02512           // draw the new value
02513 
02514           col_ = value_.size();
02515           
02516           compute_actual_width(v);
02517           
02518           v->set_curpos(pos.row_, input_);
02519           
02520           draw_input_area(v,true);
02521           
02522           v->set_curpos(pos.row_, input_ + col_ - offset_);
02523 
02524         } // expand tildes
02525 
02526 
02527         
02528         if(col != 0 &&
02529            col == value_.size()
02530           )
02531         {
02532           // expand the last non-blank section of the string
02533           // (can't expand blanks so quit if the last char is blank)
02534         
02535           if(value_[col-1] == ' ')
02536           {
02537             v->beep();
02538             break;
02539           }
02540         
02541           int word_start = col;
02542         
02543           while(word_start && value_[word_start-1] != ' ')
02544             --word_start;
02545         
02546           // either at beginning of string or on the first
02547           // char of the last word
02548         
02549           std::string last_word(value_.begin() + word_start,
02550                                 value_.end()
02551                                );
02552                         
02553           FileName path(last_word);
02554         
02555           if(last_word[0] != '/' &&
02556              !(last_word[1] == ':' && (last_word[2] == '\\' ||
02557                                        last_word[2] == '/'
02558                                       )
02559               )
02560             )
02561           {
02562             //
02563             // the completion string doesn't have a directory component
02564             // specified
02565             //
02566             path = dialog_->completion_directory() + last_word;
02567           }
02568         
02569           if(path.is_dir())
02570           {
02571             // just put a slash on the names of directories
02572         
02573             if(value_[value_.size()-1] == '/')
02574             {
02575               v->beep();
02576               break;
02577             }
02578         
02579             value_ += '/';
02580             v->write('/');
02581         
02582             col_ = value_.size();
02583             handle_paging(v);
02584         
02585           }
02586           else
02587           {
02588              FileName::sorted_names_t matching;
02589              std::string              replacement;
02590         
02591              int matches = FileName::find_matching(path + "*", &matching);
02592         
02593              if(matches == 0)
02594              {
02595                v->beep();
02596                break;
02597              }
02598         
02599              if(matches == 1)
02600                replacement = *matching.begin();
02601              else
02602              {
02603                CursorWindow::Selection selection("pick one of the following");
02604         
02605                FileName::sorted_names_t::iterator first = matching.begin(),
02606                                                   last  = matching.end();
02607                                         
02608                while(first != last)
02609                {
02610                  FileName const& cur = *first++;
02611                 
02612                  selection += cur;
02613                 
02614                }
02615         
02616                if(selection.popup(v->window()))
02617                  break;
02618                 
02619                replacement = selection.selection_;
02620                 
02621              }
02622         
02623              // erase deleted data
02624         
02625              value_.erase(value_.begin() + word_start, value_.end());
02626         
02627              // paint the input area as blank
02628         
02629              row_col pos = v->curpos();
02630         
02631              v->set_curpos(pos.row_, input_);
02632         
02633              v->write(' ', width_);
02634         
02635              // replace the trailing string with the
02636              // text of interest
02637         
02638                 
02639              value_ = path.dirname() + replacement;
02640         
02641              col_ = value_.size();
02642         
02643              compute_actual_width(v);
02644         
02645              v->set_curpos(pos.row_, input_);
02646         
02647              draw_input_area(v,true);
02648         
02649              v->set_curpos(pos.row_, input_ + col_ - offset_);
02650         
02651         
02652           }
02653         
02654         
02655         }
02656         else
02657           v->beep();
02658         
02659       }
02660       break;
02661 
02662 
02663     case CursorWindow::func_nextwd:
02664       {
02665         string::iterator start = value_.begin() + col_;
02666         string::iterator end = value_.end();
02667         
02668         string::iterator word_start = word.begin();
02669         string::iterator word_end   = word.end();
02670         
02671         start = StrTool::find_next_word(start, end, word_start, word_end);
02672         
02673         col_ = start - value_.begin();
02674         
02675         handle_paging(v);
02676                                                 
02677         
02678       }
02679       break;
02680 
02681     case CursorWindow::func_prevwd:
02682       {
02683         string::iterator start = value_.begin() + col_;
02684         string::iterator beg = value_.begin();
02685         
02686         string::iterator word_start = word.begin();
02687         string::iterator word_end   = word.end();
02688         
02689         start = StrTool::find_prev_word(start,
02690                                         beg, value_.end(),
02691                                         word_start, word_end
02692                                        );
02693         
02694         col_ = start - beg;
02695         
02696         handle_paging(v);
02697                                                 
02698         
02699       }
02700       break;
02701 
02702 
02703 
02704     case CursorWindow::func_data:
02705 
02706       if(key < 0x100 && key > 0)
02707       {
02708          value_.insert( value_.begin() + col_, (char)(key) );
02709         
02710          int maxlen = value_.size();
02711         
02712          int tail_length = maxlen - col_;
02713 
02714          int char_cells_in_window = width_ - (col_ - offset_);
02715 
02716          if(tail_length > char_cells_in_window)
02717              tail_length = char_cells_in_window;
02718         
02719          string tail_text(value_.begin() + col_,
02720                           value_.begin() + (col_ + tail_length)
02721                          );
02722         
02723          if(password_)
02724          {
02725            v->write('*', tail_text.size());
02726          }
02727          else
02728            v->write( tail_text );
02729         
02730          ++col_;
02731         
02732          handle_paging(v);
02733         
02734       }
02735       break;
02736 
02737 
02738   }
02739 }
02740 
02741 struct SaveRestoreActiveWindow
02753 {
02754 
02755   port*  active_viewport;
02756 
02757   SaveRestoreActiveWindow(CursorWindow *w)
02758   {
02759     active_viewport =  w->window_->viewport_[w->window_->active_viewport_];
02760   }
02761 
02762   ~SaveRestoreActiveWindow()
02763   {
02764     if(active_viewport)
02765     {
02766       CursorWindow::viewport vp(active_viewport);
02767 
02768       vp.activate();
02769     }
02770   }
02771 
02772 
02773 
02774 
02775 };
02776 
02777 
02778 bool
02779 CursorWindow::Dialog::
02780 popup(CursorWindow *w)
02781 {
02782   SaveRestoreActiveWindow _sr(w);
02783 
02784   // determine the dimensions of the dialog by iterating
02785   // over the element list and computing the width of
02786   // labels and the width of the input fields.  To this
02787   // horizontal width, add spacing between the labels and
02788   // the input field area.
02789   //
02790   // The height is the number of input fields plus the
02791   // 1 for the tile, plus spacing.
02792   //
02793   // The dialog will be popped up in the center of the
02794   // screen and will be in reversed video.
02795   //
02796   // After computing the screen layout, draw the dialog and
02797   // let the user edit fields.  return a flag indicating
02798   // that the user aborted the input operation.
02799 
02800   impl_->desired_height_ = impl_->first_input_row_;
02801 
02802   impl_->desired_label_width_ = 0;
02803   impl_->desired_input_width_ = 0;
02804 
02805   iterator f = begin(),
02806            l = end();
02807         
02808   while(f != l)
02809   {
02810     Element *e = *f;
02811 
02812     int wl = e->label_.size();
02813     int wi = e->input_width_;
02814 
02815     if(wl > impl_->desired_label_width_)
02816       impl_->desired_label_width_ = wl;
02817 
02818     if(wi > impl_->desired_input_width_)
02819       impl_->desired_input_width_ = wi;
02820 
02821     ++impl_->desired_height_;
02822     ++f;
02823 
02824   }
02825 
02826   impl_->desired_input_column_ = impl_->label_indent_ +
02827                                  impl_->desired_label_width_ +
02828                                  impl_->gutter_width_;
02829                                 
02830 
02831   impl_->desired_width_ = impl_->desired_input_column_ +
02832                           impl_->desired_input_width_;
02833 
02834   int title_width = impl_->title_.size() + impl_->title_col_;
02835 
02836   if(title_width > impl_->desired_width_)
02837     impl_->desired_width_ = title_width;
02838 
02839   std::auto_ptr<viewport> v(w->new_viewport(this,viewport::floating).release());
02840 
02841 
02842 
02843   impl_->page_.page_top_    = begin();
02844   impl_->page_.current_line_= begin();
02845 
02846   CursorWindow::Dialog::Impl::iterator tmp = 
02847                                impl_->find_element(impl_->first_input_field_);
02848 
02849   if(tmp != end())
02850     impl_->page_.current_line_ = tmp;
02851 
02852   impl_->first_input_field_ = "";  // dont' do this again
02853 
02854   impl_->page_.last_line_ = end();
02855 
02856   v->activate();
02857 
02858   // pressing the Enter key is the same as doing that and also
02859   // pressing the Down Arrow key -- that is, it completes the
02860   // current field but also steps tot the next field.
02861 
02862   bool                      pushed_key = false;  // down arrow pushed back
02863   CursorWindow::input_event e;
02864 
02865   for(;;)
02866   {
02867     if(pushed_key)
02868       pushed_key = false;
02869     else
02870       e = w->read_input();
02871 
02872     // this code is really wrong.  we should interpret functions
02873     // if the CursorWindow::func[value] says its a function not
02874     // arbitrarily split things out.  I'll fix this later.
02875 
02876     if(e.type_ == CursorWindow::input_event::FunctionKey)
02877     {
02878       int func_key = CursorWindow::func[e.value_];
02879 
02880       switch(func_key)
02881       {
02882         case CursorWindow::func_up:
02883 
02884           if(impl_->page_.current_line_ != begin())
02885           {
02886              // move up
02887         
02888              bool at_top_of_page = impl_->page_.current_line_ ==
02889                                    impl_->page_.page_top_;
02890         
02891              impl_->draw_current_input_area(v.get(), false);
02892 
02893              --impl_->page_.current_line_;
02894         
02895              if(at_top_of_page)
02896              {
02897                impl_->page_.page_top_ = impl_->page_.current_line_;
02898                (*this)(v.get(),0); // will paint and recompute page bottom
02899              }
02900 
02901              impl_->draw_current_input_area(v.get(), true);
02902 
02903           }
02904           else
02905            v->beep();
02906         
02907           break;
02908 
02909         case CursorWindow::func_down:
02910         {
02911           CursorWindow::Dialog::iterator next_line =
02912               impl_->page_.current_line_;
02913         
02914           ++next_line;
02915 
02916           if(next_line != impl_->page_.last_line_)
02917           {
02918              // move down
02919         
02920              impl_->draw_current_input_area(v.get(), false);
02921 
02922              ++impl_->page_.current_line_;
02923 
02924              if(next_line == impl_->page_.page_bottom_)
02925              {
02926                 // we have stepped off the bottom of the page
02927         
02928                 ++impl_->page_.page_top_;
02929 
02930                 if(impl_->page_.page_bottom_ != impl_->page_.last_line_)
02931                 {
02932                   ++impl_->page_.page_bottom_;
02933                 }
02934                 
02935                 (*this)(v.get(),0); // resets page bottom
02936              }
02937         
02938 
02939              impl_->draw_current_input_area(v.get(), true);
02940           }
02941           else
02942           {
02943             return false;
02944           }
02945           break;
02946 
02947         }
02948 
02949         case CursorWindow::func_enter:
02950 
02951            pushed_key = true;
02952            e.type_  = CursorWindow::input_event::FunctionKey;
02953            e.value_ = CursorWindow::key_down;
02954         
02955           break;
02956         
02957         case CursorWindow::func_esc:
02958         
02959           return true;
02960 
02961         default:
02962             (*impl_->page_.current_line_)->handle_key(v.get(),e.value_);
02963           break;
02964       }
02965     }
02966     else
02967     if(e.type_ == CursorWindow::input_event::DataKey)
02968     {
02969       switch(e.value_)
02970       {
02971         
02972         default:
02973            (*impl_->page_.current_line_)->handle_key(v.get(),e.value_);
02974            break;
02975       }
02976     }
02977 
02978   }
02979 
02980   return false;
02981 
02982 }
02983 
02984 void
02985 CursorWindow::Dialog::
02986 operator() (viewport* v, int cmd)
02987   // respond to paint commands which occur during initial drawing
02988   // of popups and screen resizes.
02989 {
02990 
02991 
02992    //
02993    // paint the entire dialog in reversed video
02994    //
02995 
02996    impl_->recompute_page_info(v);
02997 
02998    v->set_text_attribute(CursorWindow::dialog_normal_att);
02999    v->set_curpos(0,0);
03000    v->fill_to_eos();
03001 
03002    // paint the title
03003    v->set_text_attribute(CursorWindow::dialog_title_att);
03004    v->set_curpos(impl_->title_row_, impl_->title_col_);
03005    v->write(impl_->title_);
03006 
03007    int      i;
03008    iterator scan;
03009    int      available_label_width = impl_->actual_input_column_ -
03010                                     impl_->label_indent_;
03011 
03012    if(available_label_width < 0)
03013        available_label_width = 0;
03014 
03015    v->set_text_attribute(CursorWindow::reversed);
03016 
03017  
03018    for(i=0, scan=impl_->page_.page_top_;
03019        i < impl_->page_.height_;
03020        ++i, ++scan
03021       )
03022    {
03023      Element *e = *scan;
03024 
03025      string label = e->label_;
03026 
03027      int label_width = label.size();
03028 
03029      if(label_width > available_label_width)
03030      {
03031        string temp(label.begin(), label.begin() + available_label_width);
03032 
03033        label = temp;
03034      }
03035 
03036      v->set_curpos(impl_->first_input_row_ + i,
03037                    impl_->actual_input_column_ - impl_->gutter_width_ -
03038                    label.size()
03039                   );
03040                 
03041      v->set_text_attribute(CursorWindow::dialog_normal_att);
03042 
03043      v->write(label);
03044 
03045      v->write(": ");
03046 
03047      e->draw_input_area(v, scan == impl_->page_.current_line_);
03048 
03049    }
03050 
03051    impl_->draw_current_input_area(v, true);
03052 
03053 }
03054 
03055 class CursorWindow::Selection::Impl
03056   //
03060   //
03061 {
03062 public:
03063 
03064   typedef CursorWindow::Selection::rep_type   rep_type;
03065   typedef rep_type::iterator                  iterator;
03066 
03067   string title_;
03068 
03069   int desired_width_;
03070   int desired_height_;
03071   int actual_width_;
03072   int actual_height_;
03073 
03074   iterator page_top_;      // first visible line at top of display
03075   iterator page_bottom_;   // 1 line past bottom of display area or
03076                            // end of list
03077   iterator current_line_;  // cursor line
03078 
03079   iterator begin_;         // of list
03080   iterator end_;           // of list
03081 
03082   int title_col_;          // where title is displayed
03083   int title_row_;          // ditto
03084   int input_row_;          // where first selection appears
03085   int input_col_;          // ditto
03086 
03087   size_t auto_complete_column_;  // column where next auto_complete char
03088                                  // operation will search -- gets incremented
03089                                  // with every non-function key you type.
03090                                  // Function keys set it back to column 0.
03091                                  // When you type a non-function key, the
03092                                  // cursor will move to the line that has that
03093                                  // key in the auto-complete column
03094 
03095 
03096   void compute_page_info(viewport *v)
03097     //
03098     // This function computes a bunch of variales based on the available
03099     // width and height of the screen and given the current position within
03100     // the selection list.  It scales the viewport to the minimum size
03101     // available or needed -- that is, if we need less than we are given,
03102     // resize the viewport to the smaller size.  In any case, adjust various
03103     // variables used in displaying so we display correctly.
03104     //
03105   {
03106     row_col size = v->window()->size();
03107 
03108     // assume we are using a box around the selection, so remove the
03109     // top, bottom, left, and right character cells from the comoputed size
03110 
03111     size.row_ -= 2;
03112     size.col_ -= 2;
03113 
03114     //
03115     //  compute the usable size of the selection box.
03116     //
03117 
03118 
03119     if(desired_height_ >= size.row_ )
03120       actual_height_ = size.row_;
03121     else
03122       actual_height_ = desired_height_;
03123 
03124 
03125     if(desired_width_ > size.col_)
03126       actual_width_ = size.col_;
03127     else
03128       actual_width_ = desired_width_;
03129 
03130     row_col available = v->window()->size();
03131 
03132     row_col upper_left( (available.row_ - actual_height_)/2,
03133                                       (available.col_ - actual_width_)/2
03134                                     );
03135 
03136     v->move(upper_left, row_col(actual_height_+1, actual_width_+1));
03137 
03138 
03139     int page_height = input_row_;
03140 
03141     iterator scan =  page_top_;
03142 
03143     bool current_found = false;
03144 
03145     while(scan != end_ && page_height < actual_height_)
03146     {
03147       if(scan == current_line_)
03148         current_found = true;
03149 
03150       ++scan;
03151       ++page_height;
03152     }
03153 
03154     page_bottom_ = scan;
03155 
03156     if(!current_found)
03157       current_line_ = page_top_;
03158 
03159   }
03160 
03161   void draw_current_line(viewport *v, bool highlighted)
03162     //
03163     // compute the screen location of current_line_ and draw the
03164     // line either highlighted or not.
03165     //
03166   {
03167     int row_offset = 0;
03168     size_t avail_width = actual_width_ - input_col_;
03169 
03170     iterator scan = page_top_;
03171 
03172     while(scan != current_line_ && scan != page_bottom_)
03173     {
03174       ++row_offset;
03175       ++scan;
03176     }
03177 
03178     v->set_curpos(input_row_ + row_offset, input_col_);
03179 
03180     v->set_text_attribute(highlighted ? CursorWindow::normal : 
03181                                         CursorWindow::dialog_normal_att);
03182 
03183     if(current_line_->size() > avail_width)
03184     {
03185 
03186       string avail_text(current_line_->substr(current_line_->size() - (avail_width -4),
03187                                               current_line_->size()
03188                                              )
03189                        );
03190 
03191       v->write(".../");
03192       v->write(avail_text);
03193 
03194     }
03195     else
03196       v->write(*current_line_);
03197 
03198     v->fill_to_eol();
03199     {
03200       using namespace CursesInterface;
03201       v->set_curpos(input_row_ + row_offset, actual_width_);
03202       v->write(line_chars[VL_MIDDLE],1);
03203     }
03204     v->set_curpos(input_row_ + row_offset, input_col_ + auto_complete_column_);
03205 
03206   }
03207 
03208   void draw_current_line(std::auto_ptr<viewport> &v, bool highlighted)
03209   {
03210     draw_current_line(v.get(), highlighted);
03211   }
03212 
03213 
03214 };
03215 
03216 CursorWindow::Selection::
03217 Selection(string title)
03218 {
03219   selection_ = "";
03220 
03221   impl_ = new Impl;
03222 
03223   impl_->title_ = title;
03224 
03225   impl_->title_row_ = 0;
03226   impl_->title_col_ = 0;
03227   impl_->input_row_ = 1;
03228   impl_->input_col_ = 2;
03229 
03230 }
03231 
03232 CursorWindow::Selection::
03233 ~Selection()
03234 {
03235   delete impl_;
03236 
03237   impl_ = 0;
03238 }
03239 
03240 template<class Iterator>
03241 inline
03242 int count_distance(Iterator a, Iterator b)
03243 {
03244   int rv=0;
03245 
03246   while(a != b)
03247   {
03248     ++rv;
03249     ++a;
03250   }
03251 
03252   return rv;
03253 }
03254 
03255 
03256 bool
03257 CursorWindow::Selection::
03258 popup(CursorWindow* w)
03259   //
03262   //
03263 {
03264   SaveRestoreActiveWindow _sr(w);
03265 
03266   std::auto_ptr<viewport> v(w->new_viewport(this,viewport::floating).release());
03267 
03268   int desired_width = 0;
03269   int desired_height= 0;
03270 
03271   impl_->auto_complete_column_ = 0;
03272 
03273   rep_type::iterator scan = rep_.begin(),
03274                      end  = rep_.end();
03275                 
03276   while(scan != end)
03277   {
03278     int s = scan->size();
03279 
03280     ++desired_height;
03281 
03282     if(s > desired_width)
03283       desired_width = s;
03284 
03285       ++scan;
03286 
03287   }
03288 
03289   desired_height += impl_->input_row_;
03290   desired_width  += impl_->input_col_;
03291 
03292   if( int(impl_->title_.size())+3 > (impl_->title_col_ + desired_width) )
03293     desired_width = impl_->title_.size() + 3 + impl_->title_col_;
03294 
03295   impl_->desired_width_  = desired_width;
03296   impl_->desired_height_ = desired_height;
03297   impl_->page_top_       = rep_.begin();
03298   impl_->page_bottom_    = rep_.end();
03299   impl_->current_line_   = rep_.begin();
03300   impl_->begin_          = rep_.begin();
03301   impl_->end_            = rep_.end();
03302 
03303 
03304   v->activate(); // recomputes actual height etc
03305 
03306   CursorWindow::input_event e;
03307 
03308   selection_.resize(0);  // return value is empty string until a selection occurs
03309 
03310   for(;;)
03311   {
03312     e = w->read_input();
03313 
03314     int edit_func = w->func[e.value_];
03315 
03316     if(edit_func != CursorWindow::func_data)
03317     {
03318       impl_->auto_complete_column_ = 0;
03319     }
03320 
03321     switch(edit_func)
03322     {
03323       case CursorWindow::func_down:
03324           if(impl_->current_line_ != impl_->page_bottom_)
03325           {
03326             
03327             impl_->draw_current_line(v.get(),false);
03328             ++impl_->current_line_;
03329 
03330             if(impl_->current_line_ == impl_->page_bottom_)
03331             {
03332               // page down 1 line
03333         
03334               if(impl_->page_bottom_ != impl_->end_)
03335               {
03336                 ++impl_->page_top_;
03337                 (*this)(v.get(),0);
03338               }
03339               else
03340                 --impl_->current_line_;
03341         
03342             }
03343 
03344             impl_->draw_current_line(v.get(),true);
03345 
03346           }
03347           else
03348           if(impl_->page_bottom_ != impl_->end_)
03349           {
03350             ++impl_->page_top_;
03351             ++impl_->current_line_;
03352             (*this)(v.get(),0);
03353             impl_->draw_current_line(v.get(),true);
03354           }
03355           else
03356             w->beep();
03357         break;
03358       case CursorWindow::func_up:
03359           if(impl_->current_line_ != impl_->page_top_)
03360           {
03361             impl_->draw_current_line(v, false);
03362             --impl_->current_line_;
03363             impl_->draw_current_line(v, true);
03364           }
03365           else
03366           if(impl_->page_top_ != impl_->begin_)
03367           {
03368             --impl_->page_top_;
03369             --impl_->current_line_;
03370             (*this)(v.get(), 0);
03371             impl_->draw_current_line(v, true);
03372           }
03373         break;
03374       case CursorWindow::func_prior:
03375         {
03376           int page_height = impl_->actual_height_ - impl_->input_row_;
03377 
03378           if(impl_->page_top_ == impl_->begin_)
03379           {
03380             impl_->current_line_ = impl_->page_top_;
03381           }
03382           else
03383           {
03384             while(page_height && impl_->page_top_ != impl_->begin_)
03385             {
03386               --page_height;
03387               --impl_->page_top_;
03388               --impl_->current_line_;
03389             }
03390           }
03391         
03392           (*this)(v.get(),0);
03393 
03394           impl_->draw_current_line(v, true);
03395         
03396         
03397         
03398         }
03399         break;
03400       case CursorWindow::func_next:
03401         {
03402           int page_height = count_distance(impl_->page_top_, impl_->page_bottom_);
03403 
03404           if(impl_->page_bottom_ == impl_->end_)
03405           {
03406             goto page_bottom;
03407           }
03408           else
03409           {        
03410             while(page_height && impl_->page_bottom_ != impl_->end_)
03411             {
03412               --page_height;
03413               ++impl_->page_top_;
03414               ++impl_->current_line_;
03415               ++impl_->page_bottom_;
03416             }
03417           }
03418         
03419           (*this)(v.get(),0);
03420 
03421           impl_->draw_current_line(v, true);
03422         
03423         }
03424         break;
03425       case CursorWindow::func_top:
03426 
03427           impl_->draw_current_line(v, false);
03428 
03429           impl_->page_top_     = impl_->begin_;
03430           impl_->current_line_ = impl_->begin_;
03431 
03432           (*this)(v.get(),0);
03433 
03434           impl_->draw_current_line(v, true);
03435 
03436         break;
03437       case CursorWindow::func_bottom:
03438 
03439 page_bottom:
03440 
03441           impl_->draw_current_line(v, false);
03442 
03443           impl_->page_top_     = impl_->end_;
03444           --impl_->page_top_;
03445 
03446           impl_->current_line_ = impl_->page_top_;
03447 
03448           (*this)(v.get(),0);
03449 
03450           impl_->draw_current_line(v, true);
03451         break;
03452 
03453       case CursorWindow::func_esc:
03454         return true;
03455 
03456       case CursorWindow::func_enter:
03457 
03458         if(impl_->current_line_ != impl_->end_)
03459           selection_ = *impl_->current_line_;
03460         else
03461           return true;
03462 
03463         return false;
03464 
03465       case CursorWindow::func_data:
03466         {
03467           rep_type::iterator cur = impl_->current_line_,
03468                              end = rep_.end();
03469 
03470           int count=0;
03471           bool foundit=false;
03472 
03473           while(cur != end)
03474           {
03475             string const& s = *cur;
03476 
03477             if(s.size() > impl_->auto_complete_column_)
03478             {
03479               if(s[impl_->auto_complete_column_] == e.value_)
03480               {
03481                 // hah, found a line with the right character in the
03482                 // right column
03483                 foundit = true;
03484                 break;
03485               }
03486             }
03487             else
03488             {
03489               break;
03490             }
03491 
03492             ++cur;
03493             ++count;
03494           }
03495 
03496           if(foundit)
03497           {
03498             impl_->draw_current_line(v, false);
03499 
03500             ++impl_->auto_complete_column_;
03501 
03502             while(count--)
03503             {
03504               if(impl_->current_line_ == impl_->page_bottom_)
03505               {
03506                 ++impl_->current_line_;
03507 
03508                 int page_size = impl_->actual_height_ -
03509                                 impl_->input_row_;
03510 
03511                 while(page_size-- && impl_->page_bottom_ != rep_.end())
03512                 {
03513                   ++impl_->page_top_;
03514                   ++impl_->page_bottom_;
03515                 }
03516 
03517               }
03518               else
03519               {
03520                 ++impl_->current_line_;
03521               }
03522             }
03523 
03524             (*this)(v.get(),0);
03525 
03526             impl_->draw_current_line(v, true);
03527 
03528           }
03529 
03530 
03531         }
03532         break;
03533 
03534       default:
03535         v->beep();
03536         break;
03537     }
03538 
03539     v->set_curpos(impl_->input_row_ +
03540                     count_distance(impl_->page_top_, impl_->current_line_),
03541                   impl_->input_col_ + impl_->auto_complete_column_
03542                  );
03543 
03544   }
03545 
03546 
03547   return true;
03548 }
03549 
03550 static 
03551 void 
03552 output_short_string(viewport *v, string const &s, size_t maxlen)
03553 {
03554   if(s.size() <= maxlen)
03555     v->write(s);
03556   else
03557   {
03558      int siz = (int)(s.size());
03559      int ml  = (int)(maxlen);
03560 
03561      int index = siz - (ml - 4);
03562 
03563      if(index < 0)
03564        index = 0;
03565 
03566      string tmp( s.substr((size_t)(index), s.size()) );
03567 
03568      v->write(".../");
03569      v->write(tmp);
03570 
03571   }
03572 }
03573 
03574 
03575 void 
03576 CursorWindow::Selection::
03577 operator() (viewport *v, int cmd)
03578 {
03579   impl_->compute_page_info(v);
03580 
03581   v->set_text_attribute(CursorWindow::reversed);
03582 
03583   v->set_curpos(0,0);
03584   v->box(impl_->actual_width_+1, impl_->actual_height_+1, true, impl_->title_);
03585 
03586 
03587   v->set_text_attribute(CursorWindow::dialog_normal_att);
03588 
03589   int row = impl_->input_row_;
03590 
03591   rep_type::iterator scan = impl_->page_top_,
03592                      end  = impl_->page_bottom_;
03593                 
03594   int input_row =0;
03595 
03596   size_t avail_width = impl_->actual_width_ - impl_->input_col_;
03597 
03598   while(scan != end)
03599   {
03600     v->set_curpos(row, impl_->input_col_);
03601 
03602     if(scan == impl_->current_line_)
03603     {
03604       v->set_text_attribute(CursorWindow::normal);
03605       output_short_string(v, *scan, avail_width);
03606 
03607       v->fill_to_eol();
03608 
03609       {
03610         using namespace CursesInterface;
03611 
03612         v->set_curpos(row, impl_->actual_width_);
03613 
03614         v->write(line_chars[VL_MIDDLE], 1);
03615 
03616       }
03617 
03618       v->set_text_attribute(CursorWindow::dialog_normal_att);
03619 
03620       input_row = row;
03621 
03622     }
03623     else
03624       output_short_string(v, *scan, avail_width);
03625 
03626     ++scan;
03627     ++row;
03628 
03629   }
03630 
03631   v->set_curpos(input_row, impl_->input_col_ + impl_->auto_complete_column_);
03632 
03633 
03634 }
03635 
03636 
03637 
03638 
03639 string
03640 CursorWindow::
03641 select_file(string const &title, string const &pattern, CursorWindow* w)
03642   // Let the user select a file from a list of names.  Return empty string
03643   // if the user aborts with the esc key.
03644 {
03645 
03646   FileName::sorted_names_t names;
03647 
03648   int name_count = FileName::find_matching(pattern, &names);
03649 
03650   if(name_count == 0)
03651     return "";
03652 
03653   Selection s(title);
03654 
03655   FileName::sorted_names_t::iterator scan = names.begin(),
03656                                      end  = names.end();
03657                                 
03658   while(scan != end)
03659   {
03660     s += *scan++;
03661   }
03662 
03663   if(s.popup(w))
03664     return "";
03665 
03666   return s.selection_;
03667 
03668 
03669 }
03670 
03671 std::auto_ptr<viewport>
03672 viewport::
03673 hsplit(viewport::repaint_handler* rh)
03674 {
03675   std::auto_ptr<viewport> rv( new viewport(port_->window_, rh) );
03676 
03677   row_col origin(port_->origin());
03678   row_col size  (port_->size());
03679 
03680   if(size.col_ > 20 && size.row_ > 3)
03681   {
03682     // enough room to perform a real split
03683 
03684     int available_width = size.col_;
03685 
03686     int target_width = available_width/2;
03687 
03688     int parent_width = available_width - target_width;
03689 
03690     size.col_ = parent_width;
03691 
03692     move(origin, size); // move parent to its new size
03693 
03694     size.col_ = target_width;
03695 
03696     origin.col_ += parent_width;
03697 
03698   }
03699 
03700   rv->move(origin, size);
03701 
03702 
03703   return rv;
03704 }
03705 
03706 std::auto_ptr<viewport>
03707 viewport::
03708 vsplit(viewport::repaint_handler* rh)
03709 {
03710   std::auto_ptr<viewport> rv( new viewport(port_->window_, rh));
03711 
03712   row_col origin(port_->origin());
03713   row_col size  (port_->size());
03714 
03715   if(size.col_ > 20 && size.row_ > 3)
03716   {
03717     // enough room to perform a real split
03718 
03719     int available_height = size.row_;
03720 
03721     int target_height = available_height/2;
03722 
03723     int parent_height = available_height - target_height;
03724 
03725     size.row_ = parent_height;
03726 
03727     move(origin, size); // move parent to its new size
03728 
03729     size.row_ = target_height;
03730 
03731     origin.row_ += parent_height;
03732 
03733   }
03734 
03735   rv->move(origin, size);
03736 
03737   return rv;
03738 
03739 }
03740 
03741 int
03742 CursorWindow::
03743 get_viewports(std::list<CursorWindow::viewport> * viewports)
03744 {
03745   int rv=0;
03746 
03747   while(rv < window_->viewports_)
03748   {
03749     viewports->push_back(viewport(window_->viewport_[rv]) );
03750     ++rv;
03751   }
03752 
03753   return rv;
03754 
03755 }
03756 
03757 CursorWindow::viewport::
03758 viewport(viewport const &r)
03759 {
03760   port_ = (port*)(r.port_);
03761   owned_= false;
03762 
03763 }
03764 
03765 CursorWindow::viewport::repaint_handler *
03766 CursorWindow::viewport::
03767 get_repaint_handler()
03768 {
03769   return port_->handler_;
03770 }
03771 
03772 void
03773 CursorWindow::viewport::
03774 set_repaint_handler(CursorWindow::viewport::repaint_handler *r)
03775 {
03776   port_->handler_ = r;
03777 }
03778 
03779 
03780 string
03781 CursorWindow::Dialog::
03782 element_value(string const &name)
03783 {
03784   iterator first = begin(),
03785            last  = end();
03786         
03787   while(first != last)
03788   {
03789     Element* e = *first++;
03790 
03791     if(e->name_ == name)
03792       return e->value_;
03793 
03794   }
03795 
03796   return "";
03797 }
03798 
03799 CursorWindow::Dialog::Impl::iterator
03800 CursorWindow::Dialog::Impl::
03801 find_element(string const &name)
03802 {
03803   iterator first = elements_.begin(),
03804            last  = elements_.end();
03805         
03806   while(first != last)
03807   {
03808     Element* e = *first;
03809 
03810     if(e->name_ == name)
03811       return first;
03812 
03813     ++first;
03814 
03815   }
03816 
03817   return last;
03818 }
03819 
03822 class CursorWindow::Message::Impl
03823   //
03827   //
03828 {
03829 public:
03830 
03831   typedef CursorWindow::Message::rep_type   rep_type;
03832   typedef rep_type::iterator                  iterator;
03833 
03834   string title_;
03835 
03836   int desired_width_;
03837   int desired_height_;
03838   int actual_width_;
03839   int actual_height_;
03840 
03841   iterator page_top_;      // first visible line at top of display
03842   iterator page_bottom_;   // 1 line past bottom of display area or
03843                            // end of list
03844   iterator current_line_;  // cursor line
03845 
03846   iterator begin_;         // of list
03847   iterator end_;           // of list
03848 
03849   int title_col_;          // where title is displayed
03850   int title_row_;          // ditto
03851   int input_row_;          // where first Message appears
03852   int input_col_;          // ditto
03853 
03854   string search_text_;     // most recent search string
03855 
03856 
03857   void compute_page_info(viewport *v)
03858     //
03859     // This function computes a bunch of variales based on the available
03860     // width and height of the screen and given the current position within
03861     // the Message list.  It scales the viewport to the minimum size
03862     // available or needed -- that is, if we need less than we are given,
03863     // resize the viewport to the smaller size.  In any case, adjust various
03864     // variables used in displaying so we display correctly.
03865     //
03866   {
03867     row_col size = v->window()->size();
03868 
03869     if(desired_height_ > size.row_)
03870       actual_height_ = size.row_;
03871     else
03872       actual_height_ = desired_height_;
03873 
03874 
03875     if(desired_width_ > size.col_)
03876       actual_width_ = size.col_;
03877     else
03878       actual_width_ = desired_width_;
03879 
03880     row_col available = v->window()->size();
03881 
03882     row_col upper_left( (available.row_ - actual_height_)/2,
03883                                       (available.col_ - actual_width_)/2
03884                                     );
03885 
03886     v->move(upper_left, row_col(actual_height_, actual_width_));
03887 
03888 
03889     int page_height = input_row_;
03890 
03891     iterator scan =  page_top_;
03892 
03893     bool current_found = false;
03894 
03895     while(scan != end_ && page_height < actual_height_)
03896     {
03897       if(scan == current_line_)
03898         current_found = true;
03899 
03900       ++scan;
03901       ++page_height;
03902     }
03903 
03904     page_bottom_ = scan;
03905 
03906     if(!current_found)
03907       current_line_ = page_top_;
03908 
03909   }
03910 
03911   int move_to_current_line(viewport *v)
03912       // position the cursor on the current input line and
03913       // 
03914   {
03915     int row_offset = 0;
03916 
03917     iterator scan = page_top_;
03918 
03919     while(scan != current_line_ && scan != page_bottom_)
03920     {
03921       ++row_offset;
03922       ++scan;
03923     }
03924 
03925     v->set_curpos(input_row_ + row_offset, input_col_);
03926 
03927     return row_offset;
03928   }
03929 
03930 
03931   void draw_current_line(viewport *v, bool highlighted)
03932     //
03933     // compute the screen location of current_line_ and draw the
03934     // line either highlighted or not.
03935     //
03936   {
03937     int row_offset=0;
03938     
03939     row_offset = move_to_current_line(v);
03940 
03941 #if 0
03942     v->set_text_attribute(highlighted ? CursorWindow::normal :
03943                                         CursorWindow::dialog_normal_att
03944                          );
03945 #else
03946     v->set_text_attribute(CursorWindow::dialog_normal_att);
03947 #endif
03948 
03949     v->write(*current_line_);
03950     v->fill_to_eol();
03951 
03952     v->set_curpos(input_row_ + row_offset, input_col_);
03953 
03954 
03955 
03956   }
03957 
03958   void draw_current_line(std::auto_ptr<viewport> &v, bool highlighted)
03959   {
03960     draw_current_line(v.get(), highlighted);
03961   }
03962 
03963 
03964 };
03965 
03966 CursorWindow::Message::
03967 Message(string title)
03968 {
03969   impl_ = new Impl;
03970 
03971   impl_->title_ = title;
03972 
03973   impl_->title_row_ = 0;
03974   impl_->title_col_ = 0;
03975   impl_->input_row_ = 1;
03976   impl_->input_col_ = 2;
03977 
03978 }
03979 
03980 CursorWindow::Message::
03981 ~Message()
03982 {
03983   delete impl_;
03984 
03985   impl_ = 0;
03986 }
03987 
03988 void
03989 CursorWindow::Message::
03990 popup(CursorWindow* w)
03991   //
03994   //
03995 {
03996   SaveRestoreActiveWindow _sr(w);
03997 
03998   std::auto_ptr<viewport> v(w->new_viewport(this,viewport::floating).release());
03999 
04000   int desired_width = 0;
04001   int desired_height= 0;
04002 
04003   rep_type::iterator scan = rep_.begin(),
04004                      end  = rep_.end();
04005                 
04006   while(scan != end)
04007   {
04008     int s = scan->size();
04009 
04010     ++desired_height;
04011 
04012     if(s > desired_width)
04013       desired_width = s;
04014 
04015       ++scan;
04016 
04017   }
04018 
04019   desired_height += impl_->input_row_;
04020   desired_width  += impl_->input_col_;
04021 
04022   if( int(impl_->title_.size()) > (impl_->title_col_ + desired_width) )
04023     desired_width = impl_->title_.size() + impl_->title_col_;
04024 
04025   impl_->desired_width_  = desired_width;
04026   impl_->desired_height_ = desired_height;
04027   impl_->page_top_       = rep_.begin();
04028   impl_->page_bottom_    = rep_.end();
04029   impl_->current_line_   = rep_.begin();
04030   impl_->begin_          = rep_.begin();
04031   impl_->end_            = rep_.end();
04032 
04033   v->activate();
04034 
04035 
04036   CursorWindow::input_event e;
04037 
04038   for(;;)
04039   {
04040 
04041     {
04042       // work around for bug in nested popups -- I should fix this but the
04043       // work around is too easy.
04044 
04045       // move the cursor around to overcome a repaint bug associated with
04046       // nested popups...
04047       w->set_curpos(0,0);
04048       w->refresh();
04049       w->set_curpos(1,1);
04050       w->refresh();
04051     }
04052 
04053 
04054     impl_->move_to_current_line(v.get());
04055 
04056     e = w->read_input();
04057 
04058     int edit_func = w->func[e.value_];
04059 
04060     switch(edit_func)
04061     {
04062       case CursorWindow::func_down:
04063 
04064           if(impl_->current_line_ != impl_->page_bottom_)
04065           {
04066             impl_->draw_current_line(v.get(),false);
04067             ++impl_->current_line_;
04068 
04069             if(impl_->current_line_ == impl_->page_bottom_)
04070             {
04071               // page down 1 line
04072         
04073               if(impl_->page_bottom_ != impl_->end_)
04074               {
04075                 ++impl_->page_top_;
04076                 (*this)(v.get(),0);
04077               }
04078               else
04079                 --impl_->current_line_;
04080         
04081             }
04082 
04083             impl_->draw_current_line(v.get(),true);
04084 
04085           }
04086           else
04087           if(impl_->page_bottom_ != impl_->end_)
04088           {
04089             ++impl_->page_top_;
04090             ++impl_->current_line_;
04091             (*this)(v.get(),0);
04092             impl_->draw_current_line(v.get(),true);
04093           }
04094           else
04095             w->beep();
04096         break;
04097       case CursorWindow::func_up:
04098           if(impl_->current_line_ != impl_->page_top_)
04099           {
04100             impl_->draw_current_line(v, false);
04101             --impl_->current_line_;
04102             impl_->draw_current_line(v, true);
04103           }
04104           else
04105           if(impl_->page_top_ != impl_->begin_)
04106           {
04107             --impl_->page_top_;
04108             --impl_->current_line_;
04109             (*this)(v.get(), 0);
04110             impl_->draw_current_line(v, true);
04111           }
04112         break;
04113       case CursorWindow::func_prior:
04114         {
04115           int page_height = count_distance(impl_->page_top_, impl_->page_bottom_);
04116 
04117           if(page_height && impl_->page_top_ != impl_->begin_)
04118           {
04119               while(page_height && impl_->page_top_ != impl_->begin_)
04120               {
04121                 --page_height;
04122                 --impl_->page_top_;
04123                 --impl_->current_line_;
04124               }
04125           }
04126           else
04127           {
04128              impl_->current_line_ = impl_->page_top_;
04129           }
04130         
04131           (*this)(v.get(),0);
04132 
04133           impl_->draw_current_line(v, true);
04134         
04135         
04136         }
04137         break;
04138       case CursorWindow::func_next:
04139         {
04140           int page_height = count_distance(impl_->page_top_, impl_->page_bottom_);
04141         
04142           if(page_height && impl_->page_bottom_ != impl_->end_)
04143           {
04144               while(page_height && impl_->page_bottom_ != impl_->end_)
04145               {
04146                 --page_height;
04147                 ++impl_->page_top_;
04148                 ++impl_->current_line_;
04149                 ++impl_->page_bottom_;
04150               }
04151           }
04152           else
04153           {
04154               impl_->current_line_ = impl_->page_bottom_;
04155 
04156               if(impl_->current_line_ == impl_->end_ && impl_->begin_ != impl_->end_)
04157               {
04158                  --impl_->current_line_;
04159               }
04160 
04161           }
04162 
04163           (*this)(v.get(),0);
04164 
04165           impl_->draw_current_line(v, true);
04166         
04167         }
04168         break;
04169       case CursorWindow::func_top:
04170 
04171           impl_->draw_current_line(v, false);
04172 
04173           impl_->page_top_     = impl_->begin_;
04174           impl_->current_line_ = impl_->begin_;
04175 
04176           (*this)(v.get(),0);
04177 
04178           impl_->draw_current_line(v, true);
04179 
04180         break;
04181       case CursorWindow::func_bottom:
04182 
04183           impl_->draw_current_line(v, false);
04184 
04185           impl_->page_top_     = impl_->end_;
04186           --impl_->page_top_;
04187 
04188           impl_->current_line_ = impl_->page_top_;
04189 
04190           (*this)(v.get(),0);
04191 
04192           impl_->draw_current_line(v, true);
04193         break;
04194 
04195       case CursorWindow::func_esc:
04196         return;
04197 
04198       case CursorWindow::func_enter:
04199         return;
04200 
04201 
04202       case CursorWindow::func_find:
04203          {
04204              Dialog d("    Search help    ");
04205 
04206              d += new Dialog::String("text", 
04207                                  "    For",
04208                                  impl_->search_text_,
04209                                  20
04210                                 );
04211 
04212              bool scrollDown = false;
04213 
04214              if(!d.popup(w))
04215              {
04216                 impl_->search_text_ = d.element_value("text");
04217 
04218                 rep_type::iterator scan = impl_->current_line_;
04219 
04220                 while(scan != impl_->end_)
04221                 {
04222                     if(scan == impl_->page_bottom_)
04223                        scrollDown = true;
04224 
04225                     if(StrTool::find(impl_->search_text_, *scan, true) < scan->size())
04226                     {
04227                        break;
04228                     }
04229 
04230                     ++scan;
04231                 }
04232 
04233 
04234                 if(scan != impl_->end_)
04235                 {
04236                     impl_->draw_current_line(v, false);
04237 
04238                     if(scrollDown)
04239                     {
04240                         impl_->page_top_ = scan;
04241 
04242                         impl_->current_line_ = impl_->page_top_;
04243 
04244                         (*this)(v.get(),0);
04245 
04246                     }
04247                     else
04248                     {
04249                         impl_->current_line_ = scan;
04250                     }
04251 
04252                     impl_->draw_current_line(v, true);
04253                 }
04254                 else
04255                   w->beep();
04256                  
04257              }
04258 
04259          }
04260          break;
04261 
04262       case CursorWindow::func_findnxt:
04263          {
04264                 rep_type::iterator scan = impl_->current_line_;
04265 
04266                 ++scan;
04267 
04268                 bool scrollDown = false;
04269 
04270                 while(scan != impl_->end_)
04271                 {
04272                     if(scan == impl_->page_bottom_)
04273                        scrollDown = true;
04274 
04275                     if(StrTool::find(impl_->search_text_, *scan, true) < scan->size())
04276                     {
04277                        break;
04278                     }
04279 
04280                     ++scan;
04281                 }
04282 
04283 
04284                 if(scan != impl_->end_)
04285                 {
04286                     impl_->draw_current_line(v, false);
04287                     
04288                     if(scrollDown)
04289                     {
04290                         impl_->page_top_ = scan;
04291 
04292                         impl_->current_line_ = impl_->page_top_;
04293 
04294                         (*this)(v.get(),0);
04295 
04296                     }
04297                     else
04298                     {
04299                         impl_->current_line_ = scan;
04300                     }
04301                     
04302                     impl_->draw_current_line(v, true);
04303                 }
04304                 else
04305                    w->beep();
04306                  
04307 
04308          }
04309          break;
04310 
04311       case CursorWindow::func_data:
04312 
04313          // user has pressed a printable key
04314 
04315          switch(e.value_)
04316          {
04317             case 'q':
04318             case 'Q':
04319               return;
04320          }
04321 
04322          break;
04323 
04324     default:
04325         v->beep();
04326         break;
04327     }
04328 
04329   }
04330 
04331 }
04332 
04333 
04334 
04335 
04336 void CursorWindow::Message::
04337 operator() (viewport *v, int cmd)
04338 {
04339   impl_->compute_page_info(v);
04340 
04341   v->set_text_attribute(CursorWindow::dialog_normal_att);
04342 
04343   v->set_curpos(0,0);
04344   v->fill_to_eos();
04345 
04346   v->set_text_attribute(CursorWindow::dialog_title_att);
04347   v->set_curpos(impl_->title_row_, impl_->title_col_);
04348   v->write(impl_->title_);
04349 
04350   v->set_text_attribute(CursorWindow::dialog_normal_att);
04351 
04352   int row = impl_->input_row_;
04353 
04354   rep_type::iterator scan = impl_->page_top_,
04355                      end  = impl_->page_bottom_;
04356                 
04357 
04358   while(scan != end)
04359   {
04360     v->set_curpos(row, impl_->input_col_);
04361 
04362 #if 0
04363     if(scan == impl_->current_line_)
04364     {
04365       v->set_text_attribute(CursorWindow::normal_att);
04366       v->write(*scan);
04367       v->fill_to_eol();
04368       v->set_text_attribute(CursorWindow::dialog_normal_att);
04369     }
04370     else
04371 #endif
04372       v->write(*scan);
04373 
04374     ++scan;
04375     ++row;
04376 
04377   }
04378 
04379 }
04380 
04381 
04382 void
04383 CursorWindow::
04384 resize_viewports(row_col const &old_size, row_col const &new_size)
04385 {
04386   window_->resize_viewports(old_size, new_size);
04387   window_->repaint_ports();
04388 }
04389 
04390 
04391 CursorWindow::FileSelector::
04392 FileSelector(std::string const &title,
04393              std::string const &searchPattern,
04394              std::string const &startFile
04395             )
04396 :  title_(title)
04397 ,  searchPattern_(searchPattern)
04398 ,  startFile_(startFile)
04399 ,  noRepaint_(false)
04400 {
04401    title_ += "  (F1=help)";
04402 }
04403 
04404 void 
04405 CursorWindow::FileSelector::
04406 operator() ( viewport * v, int cmd )
04407 {
04408 
04409   if(noRepaint_)
04410     return;   // flag set only temporarily to suppress repaint of this dialog's window
04411 
04412   adaptToWindow(v);  // compute various dialog position size variables.
04413 
04414   paintDialogBackground(v);
04415 
04416   size_t rowTextSize = rowText_.size();
04417 
04418   for(int i=0; i < displayAreaHeight_; ++i)
04419   {
04420       size_t curIndex = size_t(i) + size_t(inputTopIndex_);
04421 
04422       if( curIndex >= rowTextSize )
04423          break;
04424 
04425       v->set_curpos(i+displayAreaRow_, displayAreaCol_);
04426 
04427       std::string const &name = rowText_[curIndex];
04428 
04429       if(curIndex == size_t(inputIndex_))
04430          v->set_text_attribute(CursorWindow::dialog_title_att);
04431       else
04432         v->set_text_attribute(CursorWindow::dialog_normal_att);
04433 
04434       v->write(name);
04435      
04436   }
04437 
04438 }
04439 
04440 void 
04441 CursorWindow::FileSelector::
04442 adaptToWindow( viewport * v)
04443 {
04444    size_t windowWidth  = v->size().col_;
04445    size_t windowHeight = v->size().row_;
04446 
04447    size_t titleWidth = title_.size();
04448 
04449    dialogWidth_ = (maxNameWidth_+2 > titleWidth+4) ? maxNameWidth_+2 : titleWidth+4;
04450       // the 4 in the titlewidth comes from the dashes in the box
04451       // the 2 in the maxNameWidth comes from the box |'s on either side
04452 
04453    if(size_t(dialogWidth_) > windowWidth -2)
04454        dialogWidth_ = windowWidth-2;
04455 
04456    dialogHeight_ = rowText_.size() + 1;  // 1 each for box top and bottom
04457 
04458    if(size_t(dialogHeight_) > windowHeight-2)
04459        dialogHeight_ = windowHeight -2;
04460 
04461    row_ = (windowHeight - dialogHeight_)/2;
04462    col_ = (windowWidth  - dialogWidth_) /2;
04463 
04464    row_ += v->origin().row_;
04465    col_ += v->origin().col_;
04466 
04467 
04468    displayAreaRow_    = row_+1;           // skip box top line and title
04469    displayAreaCol_    = col_+1;           // skip box left line and left border
04470    displayAreaWidth_  = dialogWidth_ -3;  // minus left and right borders
04471    displayAreaHeight_ = dialogHeight_ -2; // minus box top and bottom
04472 
04473    v->set_curpos(inputIndex_ + displayAreaRow_, displayAreaCol_);
04474 
04475    if(inputIndex_ - inputTopIndex_ >= displayAreaHeight_)
04476    {
04477       inputTopIndex_ = inputIndex_;
04478    }
04479 
04480 }
04481 
04482 
04483 void
04484 CursorWindow::FileSelector::
04485 paintDialogBackground(viewport* v)
04486 {
04487     //
04488     // fill the whole dialog with the appropriate text attributes
04489     //
04490   
04491 
04492     v->set_curpos(row_, col_);
04493     v->set_text_attribute(CursorWindow::dialog_normal_att);
04494 
04495     v->box(dialogWidth_, dialogHeight_, true, title_.c_str());
04496   
04497 
04498 }
04499 
04500 void
04501 CursorWindow::FileSelector::
04502 readFileInfo()
04503 {
04504   //
04505   //  read the display data and update maxNameWidth_
04506   //
04507 
04508   FileName::sorted_names_t names;
04509 
04510   FileName tmp(searchPattern_);   
04511   
04512   searchDir_ = tmp.dirname();
04513 
04514   if(searchDir_.empty() || searchDir_[0] == '.')
04515   {
04516       // make sure that the searchDir_ variable contains a 
04517       // real directory name, not something like "./"
04518 
04519       char buffer[3000];
04520 
04521       if( getcwd(buffer, sizeof(buffer)) )
04522       {
04523           searchDir_ = buffer;
04524           
04525           searchDir_ += '/';
04526       }
04527 
04528   }
04529 
04530 
04531   FileName::find_matching(searchPattern_, &names);
04532 
04533   maxNameWidth_=0;
04534 
04535   rowText_.clear();
04536 
04537   rowText_.push_back("<ENTER A NAME>");
04538 
04539   CXXTLS_FOREACH(std::string const &name, names)
04540   {
04541      size_t nameWidth = name.size();
04542 
04543      if(nameWidth == 1 && name[0] == '.')
04544      {
04545      }
04546      else
04547      {
04548          if(nameWidth == 2 && (name[0] == '.' && name[1] == '.'))
04549          {
04550          }
04551          else
04552          {
04553              if(maxNameWidth_ < nameWidth)
04554                 maxNameWidth_ = nameWidth;
04555              
04556              rowText_.push_back(name);
04557          }
04558      }
04559   }
04560 
04561    inputIndex_ = 0;      // cursor item index into rowText_
04562    inputTopIndex_ = 0;   // top in display area index into rowText_
04563 
04564    if(!startFile_.empty())
04565    {
04566        for(size_t i = 0; i < rowText_.size(); ++i)
04567        {
04568           if(rowText_[i] == startFile_)
04569           {
04570               inputIndex_ = i;
04571 
04572               if(i > 10)
04573               {
04574                   inputTopIndex_ = i-10;
04575               }
04576 
04577               break;
04578           }
04579        }
04580    }
04581 
04582 
04583 
04584 }
04585 
04586 void
04587 CursorWindow::FileSelector::
04588 paintRow(CursorWindow *w, int displayAreaRow)
04589 {
04590    // paint the specified row assuming that it is supposed to be displayed
04591    // at the current time.  Paint it in the current color -- either highlighted
04592    // to show the cursor or not.
04593 
04594    int rowTextIndex = inputTopIndex_ + displayAreaRow;
04595 
04596    w->set_curpos(displayAreaRow_ + displayAreaRow,
04597                  displayAreaCol_);
04598 
04599    if(inputIndex_ - inputTopIndex_ == displayAreaRow)
04600        w->set_text_attribute(CursorWindow::dialog_title_att);
04601    else
04602        w->set_text_attribute(CursorWindow::dialog_normal_att);
04603 
04604    w->write(rowText_[rowTextIndex], displayAreaWidth_);
04605 
04606 }
04607 
04608 
04609 std::string
04610 CursorWindow::FileSelector::
04611 handleInput(CursorWindow *w, Viewer *parent)
04612 {
04613   SaveRestoreActiveWindow _sr(w);
04614 
04615   std::auto_ptr<viewport> v(w->new_viewport(this,viewport::floating).release());
04616 
04617   CursorWindow::input_event e;
04618 
04619   static std::string nothing;  //empty 
04620 
04621   v->activate();   // paint the dialog
04622 
04623   for(;;)
04624   {
04625     w->set_curpos(inputIndex_ -inputTopIndex_ + displayAreaRow_, displayAreaCol_);
04626 
04627     e = w->read_input();
04628 
04629     int edit_func = w->func[e.value_];
04630 
04631 
04632     char pushBackKey=0;
04633 
04634 
04635     switch(edit_func)
04636     {
04637          default:
04638 
04639              // unbound function keys
04640 
04641              switch(e.value_)
04642              {
04643                 case 0x06:   // control f
04644                   {
04645 
04646                        eraseMe(w);
04647 
04648                        CursorWindow::Dialog   d("Enter a different directory name");
04649 
04650                        d += new CursorWindow::Dialog::String("name",
04651                                                              "Other Directory ",
04652                                                              "",
04653                                                              40
04654                                                             );
04655 
04656 
04657                        noRepaint_ = true;
04658                        int rv = d.popup(w);  // we have eliminated *this from the screen by repainting without it,
04659                                              // but popup calls the w->activateViewport (or whatever) and it repaints
04660                                              // *this -- so we set the true-false wrappers around the call to prevent
04661                                              // this dialog from painting by mistake.
04662                        noRepaint_ = false;
04663 
04664                        if( !rv )
04665                        {
04666                            std::string value = d.element_value("name");
04667 
04668                            if(value != "")
04669                            {
04670                                FileName dir(value);
04671 
04672                                if(! dir.is_dir() )
04673                                {
04674                                   if( !dir.exists() )
04675                                   {
04676                                       CursorWindow::Message  m("That's not a valid file or directory");
04677                                       m += "";
04678                                       m += "Sorry, can't find that";
04679 
04680                                       m.popup(w);
04681 
04682                                       eraseMe(w); // repaints wholse screen
04683                                       
04684 
04685                                       this->operator()(v.get(), 0);   // repaint me
04686                                       break;
04687                                      
04688                                   }
04689 
04690                                   // dir is an existing file
04691 
04692                                   startFile_=dir.basename();
04693 
04694                                   dir = dir.dirname();
04695 
04696                                }
04697 
04698                                // dir now contains a directory
04699 
04700                               if(    dir[dir.size()-1] != '/' 
04701                                  && dir[dir.size()-1] != '\\'
04702                                 )
04703                               {
04704                                   dir += '/';
04705                               }
04706 
04707                               dir += '*';
04708 
04709                               searchPattern_ = dir;
04710 
04711                               readFileInfo();
04712 
04713                               eraseMe(w);
04714             
04715                               this->operator()(v.get(),0); // repaint me in the new size
04716 
04717 
04718 
04719 
04720                            }
04721                            
04722                        }
04723 
04724                        // move the cursor around to overcome a repaint bug associated with
04725                        // nested popups...
04726                        w->set_curpos(0,0);
04727                        w->refresh();
04728                        w->set_curpos(1,1);
04729                        w->refresh();
04730 
04731 
04732                   }
04733                  break;
04734 
04735                  default:
04736                      v->beep();
04737                  break;
04738              }
04739              
04740              break;
04741 
04742         case CursorWindow::func_enter:
04743 
04744               if(   (inputIndex_ > 1)
04745                  && (size_t(inputIndex_) <  rowText_.size() )
04746                 )
04747               {
04748                  FileName file = searchDir_ + rowText_[inputIndex_];
04749 
04750                  if(file.is_dir())
04751                  {
04752                      eraseMe(w);
04753 
04754                      searchPattern_ = file + "/*";
04755 
04756                      readFileInfo();
04757 
04758                      this->operator()(v.get(),0);
04759 
04760                      break; 
04761 
04762                  }
04763               }
04764 
04765              goto promptForName;
04766 
04767 
04768          case CursorWindow::func_up:
04769 
04770              if(inputIndex_)
04771              {
04772 
04773                  if(inputIndex_ == inputTopIndex_)
04774                  {
04775                     // scroll back a line instead of moving the cursor up
04776 
04777                     int prev = inputTopIndex_ - 1;
04778 
04779                     if(prev < 0)
04780                        prev = 0;
04781 
04782                     inputIndex_ = prev;
04783                     inputTopIndex_ = prev;
04784 
04785                     this->operator() (v.get(),0);  // repaint
04786 
04787                  }
04788                  else
04789                  {
04790                      int displayOffset = inputIndex_ - inputTopIndex_;
04791 
04792                      --inputIndex_;
04793                      paintRow(w, displayOffset);
04794                      paintRow(w, displayOffset-1);
04795                  }
04796                  
04797              }
04798              else
04799                 v->beep();
04800 
04801              break;
04802 
04803          case CursorWindow::func_down:
04804              {
04805                 int offset = inputIndex_ - inputTopIndex_;
04806 
04807                 int availableTextRows = int(rowText_.size()) - inputTopIndex_;
04808 
04809                 int lastDisplayableRow;  // count of rows we can actually display
04810 
04811                 if(availableTextRows > displayAreaHeight_)
04812                    lastDisplayableRow = displayAreaHeight_ -1;
04813                 else
04814                    lastDisplayableRow = availableTextRows -1;
04815 
04816                 if(offset < lastDisplayableRow)
04817                 {
04818                     int displayOffset = inputIndex_ - inputTopIndex_;
04819 
04820                     ++inputIndex_;
04821 
04822                     paintRow(w, displayOffset);
04823                     paintRow(w, displayOffset+1);
04824 
04825                 }
04826                 else
04827                 {
04828 
04829                   if(  int(inputTopIndex_) >= int(rowText_.size())-1 )
04830                   {
04831                       w->beep();
04832                   }
04833                   else
04834                   {
04835                       if( (int)(inputIndex_) >= int(rowText_.size())-1 )
04836                       {
04837                           w->beep();
04838                       }
04839                       else
04840                       {
04841                          ++inputTopIndex_;
04842                          ++inputIndex_;                
04843                       
04844                          this->operator() (v.get(),0);  // repaint
04845                       }
04846                   }
04847 
04848                 }
04849 
04850              }
04851              break;
04852 
04853          case CursorWindow::func_next:
04854              {
04855                  int offset = inputIndex_ - inputTopIndex_;
04856                  
04857                  int next = inputTopIndex_ + displayAreaHeight_;
04858                  
04859                  if( next > int(rowText_.size())-1 )
04860                  {
04861                     v->beep();
04862                  }
04863                  else
04864                  {
04865                      inputTopIndex_ = next;
04866                      
04867                      inputIndex_ = next + offset;
04868                      
04869                      if(inputIndex_ > int(rowText_.size())-1 )
04870                      {
04871                         inputIndex_ = rowText_.size()-1;
04872                      }
04873 
04874                      this->operator() (v.get(),0);  // repaint
04875                  }
04876                  
04877 
04878              }
04879              break;
04880 
04881          case CursorWindow::func_prior:
04882              if(rowText_.size() == 0)
04883                  v->beep();
04884              else
04885              {
04886                  int offset = inputIndex_ - inputTopIndex_;
04887                  
04888                  int prev = inputTopIndex_ - displayAreaHeight_;
04889                  
04890                  if( prev < 0 )
04891                  {
04892                      prev = 0;
04893                      offset = 0;
04894                  }
04895                  
04896                  
04897                  inputTopIndex_ = prev;
04898                  
04899                  inputIndex_ = prev + offset;
04900                  
04901                  this->operator() (v.get(),0);  // repaint
04902 
04903              }
04904              break;
04905 
04906          case CursorWindow::func_help:
04907              {
04908                 CursorWindow::Message  m("File selector Help");
04909 
04910                 m += "Select from the file list or enter a file's name.";
04911                 m += "";
04912                 m += "  Standard arrows and paging keys are active.";
04913                 m += "";
04914                 m += "  Other keyboard interpretation:";
04915                 m += "";
04916                 m += "    Printable Chars   Just start typing to be prompted for a file name.";
04917                 m += "";
04918                 m += "    Enter             Enter a subdirectory, OR, select the file";
04919                 m += "                      under the cursor to be prompted for modifications ";
04920                 m += "                      to that file name (if any).";
04921                 m += "";
04922                 m += "    Space             SELECT the directory or file under the cursor";
04923                 m += "";
04924                 m += "    ^                 Navigate up a directory level";
04925                 m += "";
04926                 m += "    ^F                Goto another directory by name";
04927                 m += "";
04928 
04929                 m.popup(w);
04930 
04931                 eraseMe(w);
04932 
04933                 w->set_curpos(0,0);
04934                 w->refresh();
04935                 w->set_curpos(1,1);
04936                 w->refresh();
04937 
04938              }
04939              break;
04940 
04941          case CursorWindow::func_data:
04942 
04943              // user has pressed a printable key
04944              
04945 
04946 
04947 
04948              switch(e.value_)
04949              {
04950                 default:
04951 
04952                   if(e.value_ > ' ' && e.value_ <= '~' )
04953                   {
04954                      pushBackKey = e.value_;
04955                      goto promptForName;
04956                   }
04957 
04958                   v->beep();
04959                   break;
04960 
04961 
04962                 case 'q':
04963                 case 'Q':
04964                   return nothing;
04965 
04966                 case ' ':
04967                   if( size_t(inputIndex_) <  rowText_.size())
04968                   {
04969                      return searchDir_ + rowText_[inputIndex_];
04970                   }
04971                   break;
04972 
04973                 case '.':
04974                   {
04975 promptForName:
04976                        {
04977                        
04978                            CursorWindow::Dialog   d("Manually enter a file name ");
04979                            
04980                            string startValue;
04981                            
04982                            if(pushBackKey)
04983                            {
04984                               startValue += pushBackKey;
04985                            }
04986                            else
04987                            if(   (inputIndex_ > 0) 
04988                               && (size_t(inputIndex_) < rowText_.size()) 
04989                              )
04990                               startValue = rowText_[inputIndex_];
04991 
04992 
04993 
04994 
04995 
04996                            CursorWindow::Dialog::String *str;
04997 
04998                            d += str = new CursorWindow::Dialog::String("name",
04999                                                                        "new file name",
05000                                                                        startValue,
05001                                                                        40
05002                                                                       );
05003 
05004 
05005                            str->setColumn( startValue.size() );
05006                             
05007                            eraseMe(w);
05008 
05009                            noRepaint_ = true;
05010 
05011                            int rv = d.popup(w);  // while the sub-dialog is popped up, don't
05012                                                  // repaint the parent
05013                            noRepaint_ = false;
05014 
05015                            if( !rv )
05016                            {
05017                                FileName value = d.element_value("name");
05018                            
05019                                if(value != "")
05020                                {
05021                                   if(value.is_absolute_path())
05022                                       return value;
05023                            
05024                                    return searchDir_ + value;
05025                                }
05026                                
05027                            }
05028                            
05029                            // if we get here, we need to repaint the window and move
05030                            // the 
05031                            
05032                            
05033                            this->operator()(v.get(),0);
05034                            
05035                            // force the cursor to move so as to get the screen update
05036                            // correct at the top of this loop.
05037                            
05038                            v->set_curpos(0,0);
05039                            v->set_curpos(1,1);
05040                            v->refresh();
05041 
05042                        }
05043 
05044 
05045 
05046                        
05047                   }
05048                   break;
05049 
05050 
05051                 case '^':
05052                   {
05053 
05054                     FileName f(searchDir_);
05055 
05056                     if(f.is_root_dir())
05057                     {
05058                       v->beep();
05059                       break;
05060                     }
05061 
05062                     if(!f.empty())
05063                     {
05064                        // searchDir_ has a trailing / which must be removed
05065                        // so that a call to basename will give you the actual
05066                        // directory name
05067 
05068                        char last = f[ f.size() - 1];
05069 
05070                        if(last == '/' || last == '\\')
05071                        {
05072                            f.erase(f.size()-1);
05073                        }
05074 
05075                        startFile_ = f.basename();
05076                     }
05077 
05078                     eraseMe(w);
05079 
05080                     w->refresh();
05081 
05082                     FileName file = FileName(searchDir_).dirname();
05083 
05084                     char last = 0;
05085 
05086                     if(file.size() > 1)
05087                     {
05088                         last = file[file.size()-1];
05089 
05090                     }
05091 
05092                     if(last == '/' || last == '\\')
05093                     {
05094                         file.erase(file.size()-1);
05095 
05096                         searchPattern_ = std::string(file.dirname()) + "*";
05097                       
05098                         readFileInfo();
05099                       
05100                         this->operator()(v.get(),0);
05101 
05102                     }
05103                     else
05104                        v->beep();
05105 
05106                   }
05107                   break;
05108 
05109 
05110 
05111 
05112              }
05113 
05114              break;
05115 
05116           case CursorWindow::func_esc:
05117              return nothing;
05118 
05119 
05120     }
05121 
05122   }
05123 }
05124 
05125 std::string 
05126 CursorWindow::FileSelector::
05127 popup(CursorWindow* w, Viewer *parent)
05128 {
05129    // return empty to string to mean the operator aborted.
05130 
05131   readFileInfo();
05132 
05133   return handleInput(w, parent);
05134 
05135 }
05136 
05137 void
05138 CursorWindow::FileSelector::
05139 eraseMe(CursorWindow *w)
05140 {
05141    noRepaint_ = true;
05142    w->repaint_all();
05143    noRepaint_ = false;
05144 
05145 }
05146 
05147 
05148 void CursorWindow::repaint_all()
05149 {
05150    row_col loc = curpos();
05151 
05152    window_->repaint_ports();
05153 
05154    set_curpos(0,0);
05155    set_curpos(1,1);
05156    set_curpos(loc);
05157 
05158 }
05159 
05160 } // namespace cxxtls
05161 
05162 
Generated on Wed Feb 29 22:50:04 2012 for CXXUtilities by  doxygen 1.6.3