texteditor.cxx

Go to the documentation of this file.
00001 //
00002 // Copyright 2002-2012, Lowell Boggs Jr.
00003 //
00004 // This file or directory, containing source code for a computer program,
00005 // is Copyrighted by Lowell Boggs, Jr.  987 Regency Drive, Lewisville
00006 // TX (USA), 75067.  You may use, copy, modify, and distribute this
00007 // source file without charge or obligation so long as you agree to
00008 // the following:
00009 //
00010 //  1.  You must indemnify Lowell Boggs against any and all financial
00011 //      obligations caused by its use, misuse, function, or malfunction.
00012 //      Further, you acknowledge that there is no warranty of any kind,
00013 //      whatsoever.
00014 //
00015 //  2.  You agree not to attempt to patent any portion of this original
00016 //      work -- though you may attempt to patent your own extensions to
00017 //      it if you so choose.
00018 //
00019 //  3.  You keep this copyright notice with the file and all copies
00020 //      of the file and do not change it anyway except language translation.
00021 //
00022 // You are responsible for enforcing your own compliance with these
00023 // conditions and may not use this source file if you cannot agree to the
00024 // above terms and conditions.
00025 
00026 //@file
00107 //
00108 
00109 #include <cxxtls/options.h>
00110 #include <texteditor.hxx>
00111 #include <cxxtls/viewermanager.h>
00112 #include <cxxtls/skiprope.h>
00113 #include <fstream>
00114 #include <portable_strstream.h>
00115 #include <cxxtls/strtool.h>
00116 #include <map>
00117 #include <memory.h>
00118 #include <cxxtls/strtool.h>
00119 #include <algorithm>
00120 #include <portable_io.h>
00121 #include <cxxtls/etagsdb.h>
00122 #include <errno.h>
00123 #include <string.h>
00124 #include <ctype.h>
00125 #include <cxxtls/simple_regex.h>
00126 #include <cxxtls/cursesinterface.h>
00127 #include <cxxtls/foreach.h>
00128 #include <cxxtls/cpptagdb.h>
00129 #include <cxxtls/muSED.h>
00130 #include <fstream>
00131 
00132 
00133 using namespace cxxtls;
00134 
00135 typedef CursorWindow::row_col     row_col;
00136 typedef CursorWindow::viewport    viewport;
00137 typedef viewport::repaint_handler repaint_handler;
00138 typedef CursorWindow::input_event input_event;
00139 
00140 
00141 struct EditLine
00155 {
00156 
00157   //  Only one of the folllowing 2 pointers will be non-null
00158 
00159   std::string *original_data_;   
00160   std::string *modified_data_;   
00161 
00162   bool crRemoved_; 
00163 
00164 
00165   EditLine(std::string* original_data)
00166   :  original_data_(original_data),
00167      modified_data_(0),
00168      crRemoved_(0)
00169   {
00171   }
00172 
00173   ~EditLine()
00175   {
00176     // does nothing, you are responsible for deleting the data yourself.
00177     // that saves an extraneous copy construction everywhere
00178   }
00179 
00180   std::string* data()
00182   {
00183     return original_data_ ? original_data_ : modified_data_;
00184   }
00185 
00186   operator std::string()
00188   {
00189     if(original_data_)
00190       return StrTool::expand_tabs(*original_data_);
00191     else
00192       return *modified_data_;
00193 
00194   }
00195 
00196   void replace(std::string const &s)
00198   {
00199     if(original_data_)
00200       delete original_data_;
00201 
00202     original_data_ = 0;
00203 
00204     if(modified_data_)
00205       delete modified_data_;
00206 
00207     modified_data_ = new std::string(s);
00208   }
00209 
00210   std::string &maybe_tabbify()
00214   {
00215     static std::string rv;
00216 
00217     if(original_data_)
00218       return *original_data_;
00219 
00220     rv = StrTool::pack_tabs(*modified_data_);
00221 
00222     return rv;
00223   }
00224 
00225 
00226 
00227 };
00228 
00229 typedef skiprope<EditLine>        rep_type;   // edit session representation
00230 
00231 
00232 struct TextIterator
00244 {
00245    int           line_;    // line number we are talking about
00246    std::string   text_;    // text of a line in the edit session
00247    size_t        length_;  // length of text_
00248    size_t        col_;     // column within text_ (can be same as length!)
00249    rep_type&     rep_;     // the container of lines
00250 
00251    TextIterator(TextIterator const& r)
00252    : line_(r.line_),
00253      text_(r.text_),
00254      length_(r.length_),
00255      col_(r.col_),
00256      rep_( const_cast<rep_type&>(r.rep_))
00257    {
00258    }
00259 
00260    void operator=(TextIterator const &r)
00261    {
00262      line_   = r.line_;
00263      text_   = r.text_;
00264      length_ = r.length_;
00265      col_    = r.col_;
00266      
00267      if(&rep_ != &r.rep_)
00268      {
00269         // fatal error -- the two must be compatible!
00270      }
00271 
00272    }
00273 
00274 
00275    TextIterator(rep_type &rep, int line, size_t column)
00276    : line_(line),
00277      col_(column),
00278      rep_(rep)
00279    {
00280      if(line < rep.size())
00281        text_ = rep[line];
00282      else
00283        text_ = "";
00284 
00285      length_ = text_.size();
00286    }
00287 
00288    void correct_overruns() // called after incrementing the col_
00289    {
00290      if(col_ > length_)  // NOT ">="  !
00291      {
00292         col_ = 0;
00293         
00294         ++line_;
00295         
00296         if(line_ >= rep_.size())
00297         {
00298           line_ = rep_.size();
00299           text_ = "";
00300         }
00301         else
00302         {
00303           text_ =  rep_[line_];
00304         }
00305         
00306         length_ = text_.size();
00307         
00308      }
00309    }
00310 
00311    void correct_underruns() // called after decrementing the col_
00312    {
00313      if(col_ >= length_)    // yes >= (and length_ is an unsigned!
00314      {
00315         
00316         --line_;
00317         
00318         if(line_ < 0)
00319         {
00320           line_ = 0;
00321           text_ = "";
00322         }
00323         else
00324         {
00325           text_ =  rep_[line_];
00326         }
00327         
00328         length_ = text_.size();
00329         col_ = length_;     // yes, past the end of line so the operator* will
00330                             // return '\n'
00331                         
00332         
00333      }
00334    }
00335 
00336 
00337    char operator*() 
00338    {
00339      return col_ >= length_ ? '\n' : text_[col_];
00340    }
00341 
00342 
00343    TextIterator &operator++ ()
00344    {
00345      ++col_;
00346 
00347      correct_overruns();
00348 
00349      return *this;
00350    }
00351 
00352 
00353    TextIterator operator++ (int )
00354    {
00355      TextIterator rv= *this;
00356      ++col_;
00357      correct_overruns();
00358      return rv;
00359    }
00360 
00361    TextIterator &operator-- ()
00362    {
00363      --col_;
00364 
00365      correct_underruns();
00366 
00367      return *this;
00368    }
00369 
00370 
00371    TextIterator operator-- (int )
00372    {
00373      TextIterator rv= *this;
00374      --col_;
00375      correct_underruns();
00376      return rv;
00377    }
00378 
00379    bool operator==( TextIterator const &r )
00380    {
00381      return col_ == r.col_ && line_ == r.line_;
00382    }
00383 
00384    bool operator!=( TextIterator const &r )
00385    {
00386      return col_ != r.col_ || line_ != r.line_;
00387    }
00388 
00389 };
00390 
00391 
00392 struct UndoOperation
00395 {
00396    enum ActionToTake
00397    {
00398       replace_line,
00399       insert_line,
00400       erase_line,
00401       join_lines,       // created when you split
00402       insert_block,
00403       delete_block,
00404       replace_block,
00405       break_lines,      // created when you join
00406       nested_undo_ops,  // replace all strings' uses this to treat the undoing
00407                         // all changes in a single operation
00408                         
00409                         
00410                         
00411       last_code         // MUST BE LAST!
00412    };
00413 
00414    int code_;          // action to take
00415    int count_;         // of lines in a deleted block
00416 
00417    std::string text_;  // to replace_with
00418 
00419    int lineno_;        // line number upon which to act
00420 
00421    std::list<std::string> block_;  // block of lines to insert at lineno_
00422 
00423 
00424    typedef std::list<UndoOperation*> undo_stack;
00425 
00426    undo_stack *nested_undo_stack_;  // replace_first()
00427 
00428    int nested_first_mark_; // only valid if using the nested undo stack
00429    int nested_last_mark_;
00430 
00431 private: 
00432 
00433    void* operator new(size_t t)
00436    {
00437      return new char[t];
00438    }
00439 
00440 public:
00441 
00442    void operator delete(void *p)
00443    {
00444      char *cp = (char*)(p);
00445 
00446      delete[] cp;
00447    }
00448 
00449    static UndoOperation* make_new()
00450      // don't call this -- it is meant only
00451      // to be used by Impl::push_undo_stack()
00452    {
00453      return new UndoOperation;  
00454    }
00455 
00456 };
00457 
00458 
00459 struct TextEditor::Impl
00473 {
00474   FileName fullname_;       
00475   int      left_;           
00476   bool     active_;         
00477   int      first_row_;      
00478   int      page_top_;       
00479   size_t   page_left_;      
00480 
00481   int      cur_;            
00482   int      first_mark_;     
00483   int      last_mark_;      
00484 
00485   size_t   col_;            
00486 
00487                         
00488   int      bottom_line_;    
00489 
00490   size_t   htab_;           
00491   bool     use_tabs_;       
00492 
00493   size_t   left_margin_;    
00494 
00495 
00496   rep_type lines_;  
00497 
00498   std::string word_chars_; 
00499 
00500                         
00501   std::string search_text_; 
00502   std::string replacement_; 
00503   bool        is_regex_;    
00504   SimpleRegex regexp_;      
00505 
00506   bool key_kludge_;  
00507 
00508                 
00509   bool word_kludge_; 
00510 
00511   bool is_writeable_; 
00512 
00513   bool is_new_;       
00514                 
00515   UndoOperation::undo_stack  undo_stack_;  
00516 
00517   bool is_dirty_; // are there unsaved edit changes
00518 
00519   UndoOperation* push_undo_stack()
00520   {
00521     UndoOperation*rv = UndoOperation::make_new();
00522 
00523     undo_stack_.push_front(rv);
00524 
00525     is_dirty_ = true;
00526 
00527     return rv;
00528   }
00529 
00530 
00531   ViewerManager* manager_;
00532 
00533 
00534   bool fetchAllLines(std::list<std::string> &outout); 
00535 
00536   int      size() { return lines_.size(); } 
00537 
00541 
00542   void     display_line(viewport* vp, int line);
00543 
00544   void     read_file(); 
00545 
00546 
00547   void  paint_page(viewport* vp, Viewer *v) { (*v)(vp, 0); }
00550 
00551   void  restore_cursor(viewport* vp)
00553   {
00554     vp->set_curpos(first_row_ + (cur_ - page_top_),
00555                    col_ - page_left_ + left_
00556                   );
00557   }
00558 
00559   void push_undo_line_change(int line, std::string const &old_text)
00563   {
00564     if(undo_stack_.begin() == undo_stack_.end())
00565     {
00566       // stack is empty, create the first element
00567 
00568       UndoOperation* op = push_undo_stack();
00569 
00570       op->code_   = UndoOperation::replace_line;
00571       op->text_   = StrTool::expand_tabs(old_text);
00572       op->lineno_ = line;
00573 
00574 
00575     }
00576     else
00577     {
00578       // the stack isn't empty, see if the most recent item is the
00579       // same as this item
00580 
00581       UndoOperation* op = undo_stack_.front();
00582 
00583       if(op->code_    != UndoOperation::replace_line ||
00584          op->lineno_  != line
00585         )
00586       {
00587         // this is a new one, so push it on the stack
00588 
00589         UndoOperation* op = push_undo_stack();
00590         
00591         op->code_   = UndoOperation::replace_line;
00592         op->text_   = old_text;
00593         op->lineno_ = line;
00594         
00595 
00596       }
00597 
00598     }
00599 
00600   }
00601 
00602 
00603   void replace_line(int line_no, std::string const &new_text, bool save_undo=true)
00608   {
00609      if(save_undo)
00610        push_undo_line_change(line_no, *line_text(line_no).data());
00611 
00612      lines_[line_no].replace(new_text);
00613   }
00614 
00615   rep_type::iterator line_address(int line_no)
00617   {
00618     return lines_.find(line_no);
00619   }
00620 
00621   void erase_line(rep_type::iterator location,
00622                   int                corresponding_line,
00623                   bool               save_undo_info = true
00624                  )
00626   {
00627     if(save_undo_info)
00628     {
00629       EditLine& line  = *location;
00630 
00631       UndoOperation *op = push_undo_stack();
00632 
00633       op->text_   = *line.data();
00634       op->lineno_ = corresponding_line;
00635       op->code_   = UndoOperation::insert_line;
00636 
00637     }
00638 
00639     lines_.erase(location);
00640   }
00641 
00642 
00643   EditLine& line_text(int line_no)
00645   {
00646 
00647     static EditLine empty( new std::string("") );
00648 
00649     if( line_no >= lines_.size())
00650     {
00651        return empty;
00652     }
00653 
00654     return lines_[line_no];
00655   }
00656 
00657 
00658   void  make_col_visible(viewport*, Viewer*);
00661 
00662   void  make_line_visible(viewport*, Viewer*, bool center=true);
00670 
00671   size_t vwidth(row_col &vp_size) { return vp_size.col_ - left_; }
00674 
00675   size_t vheight(row_col &vp_size) { return vp_size.row_ - first_row_; }
00678 
00679   bool  is_marked(int line) { return line >= first_mark_ && line < last_mark_; }
00681 
00682   typedef bool (Impl::*func_handler)(viewport*, Viewer*, input_event const *);
00684 
00685   static std::map<int, std::pair<func_handler, bool> > func;
00697 
00698   bool find_next_helper(viewport*, Viewer*, input_event const *);
00708 
00709   bool find_prev_helper(viewport*, Viewer*, input_event const *);
00711 
00712   bool replace_next_helper(viewport*, Viewer*, input_event const *);
00718 
00719   void save         (viewport*, TextEditor*, input_event const *);
00721 
00722 
00723   bool is_word_char(char c)
00726   {
00727     if(std::find( word_chars_.begin(), word_chars_.end(), c) == word_chars_.end())
00728       return false;
00729 
00730     return true;
00731   }
00732 
00733   //  The following functions return iterators into the edit session that will
00734   //  span lines -- don't modify the session by creating, deleting, or changing
00735   //  lines as this will invalidate the iterators.
00736 
00737   TextIterator top_pointer();  
00738   TextIterator cur_pointer();  
00739   TextIterator bot_pointer();  
00740 
00741   //
00742   // The following class, StringCompare, is a character comparison functor.  It is
00743   // meant to be used as a parameter to the std::search algoritm
00744   //
00745 
00746   bool mixed_case_;    // controls CharCompare
00747   bool match_words_;   // find and replace only whole words
00748 
00749   struct CharCompare
00750   {
00751     TextEditor::Impl* impl_;
00752 
00753     bool operator() (char a, char b) const
00754     {
00755         if(impl_->mixed_case_)
00756         {
00757           if( isupper(a) )
00758             a = tolower(a);
00759         
00760           if( isupper(b) )
00761             b = tolower(b);
00762         
00763           return a == b;
00764         }
00765         
00766         return a == b;
00767     }
00768 
00769     CharCompare(TextEditor::Impl* t)
00770     : impl_(t)
00771     {
00772     }
00773 
00774   };
00775 
00776 
00777   //
00778   // Declarations for the various editor member functions whose job is to
00779   // directly implement an edit command
00780   //
00781   bool page_down      (viewport*, Viewer*, input_event const *);
00782   bool page_up        (viewport*, Viewer*, input_event const *);
00783   bool char_left      (viewport*, Viewer*, input_event const *);
00784   bool char_right     (viewport*, Viewer*, input_event const *);
00785   bool char_matching  (viewport*, Viewer*, input_event const *);
00786   bool tab_right      (viewport*, Viewer*, input_event const *);
00787   bool tab_left       (viewport*, Viewer*, input_event const *);
00788   bool char_delprv    (viewport*, Viewer*, input_event const *);
00789   bool char_delete    (viewport*, Viewer*, input_event const *);
00790   bool char_insert    (viewport*, Viewer*, input_event const *);
00791   bool line_home      (viewport*, Viewer*, input_event const *);
00792   bool line_end       (viewport*, Viewer*, input_event const *);
00793   bool line_split     (viewport*, Viewer*, input_event const *);
00794   bool line_up        (viewport*, Viewer*, input_event const *);
00795   bool line_down      (viewport*, Viewer*, input_event const *);
00796   bool line_del       (viewport*, Viewer*, input_event const *);
00797   bool line_mark      (viewport*, Viewer*, input_event const *);
00798   bool line_clreol    (viewport*, Viewer*, input_event const *);
00799   bool line_goto      (viewport*, Viewer*, input_event const *);
00800   bool line_join      (viewport*, Viewer*, input_event const *);
00801   bool line_insert    (viewport*, Viewer*, input_event const *);
00802   bool file_top       (viewport*, Viewer*, input_event const *);
00803   bool file_bottom    (viewport*, Viewer*, input_event const *);
00804   bool file_insert    (viewport*, Viewer*, input_event const *);
00805   bool word_next      (viewport*, Viewer*, input_event const *);
00806   bool word_prev      (viewport*, Viewer*, input_event const *);
00807   bool word_delete    (viewport*, Viewer*, input_event const *);
00808   bool snatch_edit    (viewport*, Viewer*, input_event const *);
00809   bool show_symbols   (viewport*, Viewer*, input_event const *);
00810   bool word_sfind     (viewport*, Viewer*, input_event const *);
00811   bool find_first     (viewport*, Viewer*, input_event const *);
00812   bool find_next      (viewport*, Viewer*, input_event const *);
00813   bool find_prev      (viewport*, Viewer*, input_event const *);
00814   bool block_delete   (viewport*, Viewer*, input_event const *);
00815   bool block_copy     (viewport*, Viewer*, input_event const *);
00816   bool block_paste    (viewport*, Viewer*, input_event const *);
00817   bool block_insert   (viewport*, Viewer*, input_event const *);
00818   bool block_adjust   (viewport*, Viewer*, input_event const *);
00819   bool block_save     (viewport*, Viewer*, input_event const *);
00820   bool block_transform(viewport*, Viewer*, input_event const *); // use mused to change the block
00821   bool replace_first  (viewport*, Viewer*, input_event const *);
00822   bool replace_next   (viewport*, Viewer*, input_event const *);
00823   bool undo           (viewport*, Viewer*, input_event const *);
00824   bool etags_first    (viewport*, Viewer*, input_event const *);
00825   bool cpptags_first  (viewport*, Viewer*, input_event const *);
00826   bool tags_next      (viewport*, Viewer*, input_event const *);
00827   bool etags_all      (viewport*, Viewer*, input_event const *); // list all matching functions
00828   bool cpptags_all    (viewport*, Viewer*, input_event const *);
00829   bool to_edit        (viewport*, Viewer*, input_event const *);
00830   bool reread         (viewport*, Viewer*, input_event const *); // cannot be undone
00831   bool switch_source  (viewport*, Viewer*, input_event const *);
00832 
00833 };
00834 
00835 typedef TextEditor::Impl  Impl;  // to simplfy typing
00836 
00837 
00838 std::map<int, std::pair<Impl::func_handler, bool> > Impl::func;
00839     // the mapping between CursorWindow::func_*
00840     // values and TextEditor command handlers.
00841 
00842 const bool Modifying=   true;  // use as parm to AddMapping constructor
00843 const bool NonModifying=false;
00844 
00845 struct AddMapping
00858 {
00859   AddMapping(int cmd, Impl::func_handler f, bool modifying)
00860   {
00861     Impl::func[cmd] = std::pair<Impl::func_handler, bool>(f, modifying);
00862   }
00863 };
00864 
00865 bool TextEditor::fetchAllLines(std::list<std::string> &output)
00866 {
00867    if(impl_.get())
00868    {
00869       return impl_->fetchAllLines(output);
00870    }
00871 
00872    return false;
00873 }
00874 
00875 
00876 void
00877 TextEditor::
00878 operator() ( CursorWindow::viewport *     vp,
00879              int                          cmd
00880            )
00882 {
00883   // WARNING:  do not save vp -- it gets deleted regularly!
00884   
00885   // get page size and origin
00886 
00887   row_col size    = vp->size();
00888   row_col origin  = vp->origin();
00889 
00890   int     avail_rows  = impl_->vheight(size);
00891   // determine if the page is active or not
00892 
00893   if(cmd == repaint_handler::activate)
00894     impl_->active_ = true;
00895   else
00896   if(cmd == repaint_handler::deactivate)
00897     impl_->active_ = false;
00898 
00899   // handle the page left margin
00900 
00901   if(origin.col_)
00902   {
00903     impl_->left_ = 1;
00904 
00905         
00906     for(int i=0; i < size.row_; ++i)
00907     {
00908       vp->set_curpos(i, 0);
00909 
00910       if(i == size.row_ -1 || i == 0)
00911        vp->set_text_attribute(CursorWindow::bold_ul);
00912       else
00913        vp->set_text_attribute(ViewerManager::inactive_mark_att);
00914 
00915       using namespace CursesInterface;
00916 
00917       vp->write(line_chars[VL_MIDDLE]);
00918     }
00919 
00920   }
00921   else
00922     impl_->left_ =0;
00923 
00924   // handle the title line
00925 
00926   vp->set_curpos(0, impl_->left_);
00927   vp->set_text_attribute(impl_->active_? ViewerManager::active_title_att
00928                                        : ViewerManager::inactive_title_att
00929                                        );
00930   if( impl_->is_new_ )
00931     vp->write("Creating ");
00932   else
00933   if( impl_->is_writeable_ )
00934     vp->write("Editing ");
00935   else
00936     vp->write("Reading ");
00937 
00938 
00939   if(size.col_ > 70 )
00940   {
00941       vp->write(impl_->fullname_.shorten(size.col_ - 8 - 26) );
00942   }
00943   else
00944       vp->write(impl_->fullname_.shorten(size.col_ - 8) );
00945 
00946 
00947 
00948   vp->fill_to_eol();
00949 
00950   // paint the body of the text
00951 
00952   if(impl_->cur_ - impl_->page_top_ >= avail_rows)
00953   {
00954      // current cursor row is not visible -- make ti be visible.
00955 
00956      impl_->page_top_ = impl_->cur_ - avail_rows/2;
00957 
00958      if(impl_->page_top_ < 0)
00959      {
00960         impl_->page_top_ = 0;
00961      }
00962 
00963   }
00964 
00965 
00966   int row = impl_->first_row_;
00967 
00968   int line = impl_->page_top_;
00969 
00970   impl_->bottom_line_ = line + avail_rows - 1 ;  // for bottom line highlighting
00971 
00972   int row_count = 0;
00973 
00974   while(line < impl_->size() && row_count < avail_rows )
00975   {
00976      vp->set_curpos(row, impl_->left_);
00977      impl_->display_line(vp, line);
00978      ++row;
00979      ++line;
00980      ++row_count;
00981   }
00982 
00983 
00984   // paint the lines after body of the text
00985 
00986   bool first=true;
00987 
00988   while(row < size.row_)
00989   {
00990     vp->set_curpos(row, impl_->left_);
00991 
00992     if(first)
00993     {
00994       vp->set_text_attribute(ViewerManager::active_title_att);
00995       vp->write("*eof*");
00996       vp->set_text_attribute(ViewerManager::normal_att);
00997       first = false;
00998     }
00999     else
01000     {
01001       if(row == size.row_ -1)
01002         vp->set_text_attribute(ViewerManager::bottom_att);
01003       else
01004         vp->set_text_attribute(ViewerManager::normal_att);
01005     }
01006     vp->fill_to_eol();
01007 
01008     ++row;
01009   }
01010 
01011   // place the cursor
01012 
01013   impl_->restore_cursor(vp);
01014 
01015 }
01016                         
01017 bool
01018 TextEditor::
01019 handle_event( input_event const * e,
01020               viewport          * vp
01021             )
01022 {
01023 
01024   if(!impl_->is_writeable_)
01025   {
01026      // in view only mode, treat 'q' as an exit command
01027 
01028      if(e->type_ == CursorWindow::input_event::DataKey)
01029      {
01030         if(e->value_ == 'q' || e->value_ == 'Q')
01031         {
01032            return true;
01033         }
01034      }
01035   }
01036 
01037 
01038   if(e->type_ == CursorWindow::input_event::ForceExitKey)
01039   {
01040     return true;
01041   }
01042 
01043   bool rv = false;
01044 
01045 
01046   if(impl_->key_kludge_)
01047   {
01048     //
01049     // Most recent key was '^X', so handle the second key of a multi-key sequence
01050     // Note:  this is stupid.  I build a great mechanism for function key sequence
01051     // processing and I am not using it....
01052     //
01053 
01054     impl_->key_kludge_ = false;      // reset the ^X flag
01055 
01056     if(e->value_ == ('='))       // ^X = switches to the next source alternative
01057     {
01058       impl_->switch_source(vp,this,e);
01059     }
01060     else
01061     if(e->value_ == ('L'-'@'))       // ^X ^L rereads edit session
01062     {
01063       impl_->reread(vp,this,e);
01064     }
01065     else
01066     if(e->value_ == ('S' - '@'))     // ^X ^S saves the session
01067     {
01068       if(impl_->word_kludge_)
01069       {
01070         impl_->word_sfind(vp,this,e);
01071         impl_->word_kludge_ = false;
01072       }
01073       else
01074         impl_->save(vp,this,e);
01075     }
01076     else
01077     if(e->value_ == ('K' - '@') ||     // ^X ^K quits the session
01078        e->value_ == 'k'         ||     // ^X k quits the session
01079        0
01080       )
01081     {
01082       if(impl_->is_dirty_)
01083       {
01084         vp->beep();
01085         
01086         CursorWindow::Dialog d("Unsaved edit changes exist");
01087         d += new CursorWindow::Dialog::String("ok",
01088                                               "Quit Anyway (Yes or No)",
01089                                               "No",
01090                                               4
01091                                              );
01092         
01093         if(d.popup( manager_->window() ))
01094         {
01095           return false;
01096         }
01097         
01098         std::string ok_value = d.element_value("ok");
01099         
01100         if(ok_value.size() > 2)
01101         {
01102           ok_value.erase(2, ok_value.size());
01103         }
01104         
01105         if(ok_value != "Ye" && ok_value != "ye" )
01106         {
01107           vp->beep();
01108           return false;
01109         }
01110         
01111       }
01112       return true;
01113     }
01114     else
01115     if(e->value_ == ('G' - '@'))
01116       rv = impl_->line_goto(vp,this,e);
01117     else
01118     if(e->value_ == ('E' - '@'))
01119       rv= impl_->snatch_edit(vp,this,e);
01120     else
01121     if(e->value_ == ('W' - '@'))
01122     {
01123       impl_->key_kludge_ = true;   // if handling ^X, ignore ^W for the moment
01124       impl_->word_kludge_ = true;   // if handling ^X, ignore ^W for the moment
01125     }
01126     else
01127     if(e->value_ == '!')
01128     {
01129       impl_->etags_first(vp,this,e);
01130     }
01131     else
01132     if(e->value_ == '.')
01133     {
01134       impl_->cpptags_first(vp,this,e);
01135     }
01136     else
01137     if(e->value_ == '?')
01138     {
01139       impl_->etags_all(vp,this,e);
01140     }
01141     else
01142     if(e->value_ == '/')
01143     {
01144       impl_->cpptags_all(vp,this,e);
01145     }
01146     else
01147     if(e->value_ == ',')
01148     {
01149       impl_->tags_next(vp,this,e);
01150     }
01151     else
01152     if(e->value_ == ('A' - '@'))
01153     {
01154       impl_->key_kludge_ = true;   // if handling ^X, ignore ^A for the moment
01155       impl_->word_kludge_ = true;   // if handling ^X, ignore ^A for the moment
01156     }
01157     else
01158     if(e->value_ == ('I' - '@'))
01159     {
01160       impl_->block_insert(vp,this,e);  // ^X ^A ^I maps to 'insert text into block'
01161     }
01162     else
01163     if(e->value_ == ('B' - '@'))
01164     {
01165       impl_->block_adjust(vp,this,e);  // ^X ^A ^B maps to 'adjust block left and right'
01166     }
01167     else
01168     if(e->value_ == ('U' - '@'))
01169     {
01170       impl_->undo(vp,this,e); // ^X ^U is also undo.
01171     }
01172     else
01173     if(e->value_ == '0')
01174     {
01175       impl_->show_symbols(vp,this,e); // run cpptagdb.exe on this file and show the results
01176     }
01177     else
01178     if(e->value_ == ('R' - '@') )
01179     {
01180         impl_->file_insert(vp,this,e);
01181     }
01182     else
01183     if(e->value_ == ('O' - '@'))
01184     {
01185        impl_->block_save(vp,this,e);
01186     }
01187     else
01188     if(e->value_ == ('T' - '@'))
01189     {
01190        impl_->block_transform(vp,this,e);
01191     }
01192 
01193   }
01194   else
01195   {
01196     impl_->word_kludge_ = false;
01197 
01198     std::pair<Impl::func_handler, bool> map_entry = Impl::func[ CursorWindow::func[e->value_] ];
01199 
01200     Impl::func_handler f         =  map_entry.first;
01201     bool               modifying =  map_entry.second;
01202 
01203 
01204     if(f)
01205     {
01206        if(!impl_->is_writeable_ && modifying)
01207          vp->beep();
01208        else
01209          rv = (impl_.get()->*f)(vp, this, e);
01210     }
01211     else
01212     if(e->value_ == 0x1b)
01213     {
01214       vp->beep();  // this should bring up a command menu
01215     }
01216     else
01217     if(e->value_ == ('X' - '@'))
01218     {
01219       impl_->key_kludge_ = true;
01220     }
01221     else
01222       manager_->window()->beep();
01223   }
01224 
01225   if(rv == false)
01226   {
01227      char buffer[256];
01228 
01229 
01230      size_t lineOffset = impl_->cur_;
01231      size_t charOffset = impl_->col_;
01232 
01233 
01234 //     char c = 0;
01235 //     std::string line = impl_->line_text(lineOffset);
01236 //
01237 //     if(line.size() > charOffset)
01238 //     {
01239 //        c = line[charOffset];
01240 //     }
01241 //
01242 //     snprintf(buffer, 256, "L=%d, C=%d, '\\0%o'", int(lineOffset+1), int(charOffset+1), c);
01243 
01244 
01245      snprintf(buffer, 256, "L=%-8d C=%-3d", int(lineOffset+1), int(charOffset+1));
01246 
01247      CursorWindow::row_col windowSize = vp->size();
01248 
01249      if(windowSize.col_ > 70)
01250      {
01251          
01252          vp->set_curpos(0, windowSize.col_ - 26);
01253          
01254          vp->set_text_attribute(impl_->active_? ViewerManager::active_title_att
01255                                               : ViewerManager::inactive_title_att
01256                                );
01257          vp->fill_to_eol();
01258          
01259          
01260          
01261          vp->set_curpos(0, windowSize.col_ - strlen(buffer));
01262          vp->write(buffer);
01263          
01264          impl_->restore_cursor(vp);
01265      }
01266 
01267   }
01268 
01269 
01270   return rv;
01271 }
01272 
01273         
01274 void
01275 TextEditor::
01276 help()
01277 {
01278   typedef std::list<std::string> help_text;
01279 
01280   std::auto_ptr< help_text > viewer_help( new help_text );
01281 
01282   extern char const *editversion;
01283 
01284   std::string htext("Help for the text editor ");
01285 
01286   htext += editversion;
01287 
01288   viewer_help->push_back(htext);
01289 
01290   viewer_help->push_back("");
01291 
01292   viewer_help->push_back("(Use ^S to search)");
01293 
01294   viewer_help->push_back("");
01295 
01296 
01297   viewer_help->push_back("  In addition to the standard key bindings");
01298   viewer_help->push_back("  shown later, the text editor interprets");
01299   viewer_help->push_back("  the following parochial key bindings:");
01300   viewer_help->push_back("    ^X ^E -- edit the file whose name appears");
01301   viewer_help->push_back("             under the cursor");
01302   viewer_help->push_back("    ^X ^G -- goto specified line");
01303   viewer_help->push_back("    ^X ^S -- saves the edit session");
01304   viewer_help->push_back("    ^X ^K -- quits the edit session");
01305   viewer_help->push_back("    ^X  k -- quits the edit session");
01306   viewer_help->push_back("    ^X ^l -- rereads the file being edited, discarding changes");
01307   viewer_help->push_back("    ^X ^W ^E -- edit the file whose name appears");
01308   viewer_help->push_back("             under the cursor");
01309   viewer_help->push_back("    ^X ^W ^S -- find the next instance of the word under the cursor");
01310   viewer_help->push_back("    ^X  . -- Symbol name search (TAGPP.tagpp database)");
01311   viewer_help->push_back("    ^X  / -- All matching symbols searched (TAGPP.tagpp database)");
01312   viewer_help->push_back("    ^X  ! -- etags search FIRST matching symbol");
01313   viewer_help->push_back("    ^X  ? -- etags search ALL matching symbols");
01314   viewer_help->push_back("    ^X  , -- Search for next matching symbol");
01315   viewer_help->push_back("    ^X ^T -- Transform a block using SED commands:");
01316   viewer_help->push_back("             Note:  if any *.mused files are being edited,");
01317   viewer_help->push_back("             one of them will be selected as the script,");
01318   viewer_help->push_back("             otherwise, the user will be prompted for a 3 line script.");
01319   viewer_help->push_back("    ^X ^U -- undo (same as ^_ and ^?)");
01320   viewer_help->push_back("    ^X 0  -- show the symbols in the current file (only)");
01321   viewer_help->push_back("    ^X ^R -- read a file into the edit session");
01322   viewer_help->push_back("    ^X ^O -- write a selected block to a file.");
01323   viewer_help->push_back("");
01324   viewer_help->push_back("    ^X ^A ^I  -- insert column of text into");
01325   viewer_help->push_back("                 the currently marked block");
01326   viewer_help->push_back("    ^X ^A ^B  -- add or delete columns of");
01327   viewer_help->push_back("                 blanks to the current block");
01328   viewer_help->push_back("    F12  --  jump back and forth between source file variants:  .c, .h, etc");
01329   viewer_help->push_back("");
01330   viewer_help->push_back("  Special environment variables:");
01331   viewer_help->push_back("");
01332   viewer_help->push_back("   ETAGSPATH --  A ':' separated list of directories");
01333   viewer_help->push_back("                 in which to look for the TAGS file");
01334   viewer_help->push_back("                 which is used when looking for symbol definitions");
01335   viewer_help->push_back("                 and for source file alternates (.c/.h) -- see F12");
01336   viewer_help->push_back("                 EG:  .:..:/home/user");
01337   viewer_help->push_back("   SOURCEPATH--  A ':' separated list of directories and '*'s");
01338   viewer_help->push_back("                 in which to look for the source file alternates file");
01339   viewer_help->push_back("                 which is used when looking for symbol definitions");
01340   viewer_help->push_back("                 and for source file alternates (.c/.h) -- see F12");
01341   viewer_help->push_back("                 EG:  ..:/project/main/include:/project/main/src:/home/*");
01342   viewer_help->push_back("                 Searched when ^X ^E or ^X ^W ^E are pressed.");
01343   viewer_help->push_back("   SOURCEALTS--  A ':' separated list of file name extensions");
01344   viewer_help->push_back("                 which are used by the F12 key (switch source) to decide");
01345   viewer_help->push_back("                 how to find the alternative source file given the current");
01346   viewer_help->push_back("                 source file's name.  EG: .:.c:.h:.cc:.cxx:.cpp.  See F12");
01347 
01348 
01349 
01350   impl_->manager_->help_helper(*viewer_help);
01351 
01352 }
01353 
01354 
01355 TextEditor::
01356 TextEditor(ViewerManager*vm, FileName fullname)
01357 : Viewer(vm),
01358   impl_(new Impl)
01359 {
01360   impl_->fullname_    = fullname;
01361   impl_->left_        = 0;
01362   impl_->active_      = false;
01363   impl_->first_row_   = 1;
01364   impl_->page_top_    = 0;
01365   impl_->page_left_   = 0;
01366   impl_->cur_         = 0;
01367   impl_->col_         = 0;
01368   impl_->htab_        = 8;      // changed when file read
01369   impl_->use_tabs_    = true;   // changed when file read
01370   impl_->bottom_line_ = 10000;  // an obviously invalid value
01371   impl_->first_mark_  = -1;
01372   impl_->last_mark_   = -1;     // pair of marked lines
01373   impl_->left_margin_ = 0;
01374   impl_->manager_     = vm;
01375   impl_->key_kludge_  = false;
01376   impl_->word_kludge_ = false;
01377   impl_->mixed_case_  = true;   // mixed case find string.
01378   impl_->match_words_ = false;  // only match whole words
01379   impl_->is_writeable_= true;   // true unless otherwise set to false by read_file
01380   impl_->is_new_      = false;  // set to true by read_file if file does not exist
01381   impl_->is_regex_    = false;  // not regex searches by default
01382 
01383   impl_->read_file();
01384 
01385   impl_->is_dirty_    = false;  // no unsaved edit changes exist yet
01386 
01387   {
01388     // define word characters
01389 
01390     std::string &word_chars = impl_->word_chars_;
01391 
01392     char c;
01393 
01394     word_chars += '_';
01395 
01396     for(c='a'; c <= 'z'; ++c) word_chars += c;
01397     for(c='A'; c <= 'Z'; ++c) word_chars += c;
01398     for(c='0'; c <= '9'; ++c) word_chars += c;
01399     word_chars += ':';
01400 
01401   }
01402 
01403 }
01404 
01405 TextEditor::
01406 ~TextEditor()
01407 {
01408   rep_type::iterator first = impl_->lines_.begin(),
01409                      last  = impl_->lines_.end();
01410                 
01411   while(first != last)
01412   {
01413     EditLine cur = *first++;
01414 
01415     delete cur.data();
01416   }
01417 }
01418 
01419 Viewer*
01420 TextEditor::
01421 app(ViewerManager*vm, std::string &fullname)
01422 {
01423   if(fullname.size() == 0)
01424   {
01425 #if 0
01426     CursorWindow::Dialog d("Edit File name");
01427 
01428     d += new CursorWindow::Dialog::String("name",
01429                                           "path",
01430                                           "*",
01431                                           40
01432                                          );
01433                                 
01434     if(d.popup(vm->window()))
01435       return 0;
01436 
01437     fullname = d.element_value("name");
01438 #else
01439     CursorWindow::FileSelector d("Edit File Name", "*", "");
01440 
01441     fullname = d.popup(vm->window(), 0);
01442 
01443 #endif
01444 
01445     if(fullname == "")
01446       return 0;
01447 
01448 
01449     FileName tmp(fullname);
01450     tmp.convert_to_absolute_path();
01451 
01452     fullname = tmp;
01453 
01454   }
01455   return new TextEditor(vm,fullname);
01456 
01457 }
01458 
01459 std::string
01460 TextEditor::
01461 app_name="TextEditor";
01462 
01463 std::string
01464 TextEditor::
01465 description() const
01466 {
01467   return impl_->fullname_;
01468 }
01469 
01470 bool
01471 TextEditor::
01472 is_dirty() const
01473 {
01474   return impl_->is_dirty_;
01475 }
01476 
01477 void
01478 Impl::
01479 display_line(viewport* vp, int line)
01480 {
01481   if(is_marked(line))
01482   {
01483     if(active_)
01484       vp->set_text_attribute(ViewerManager::highlighted_att);
01485     else
01486       vp->set_text_attribute(ViewerManager::inactive_mark_att);
01487   }
01488   else
01489   if(line == bottom_line_)
01490     vp->set_text_attribute(ViewerManager::bottom_att);
01491   else
01492     vp->set_text_attribute(ViewerManager::normal_att);
01493 
01494 
01495   if(line < size() )
01496   {
01497     std::string str = line_text(line);
01498 
01499     if(str.size() >= (size_t)(page_left_))
01500     {
01501       std::string::iterator first = str.begin() + page_left_,
01502                             last  = str.end();
01503 
01504       vp->write(first,last);
01505     }
01506 
01507   }
01508 
01509   vp->fill_to_eol();
01510 }
01511 
01512 
01514 
01515 AddMapping page_down_func(CursorWindow::func_next, &Impl::page_down, NonModifying);
01516 
01517 bool
01518 Impl::
01519 page_down(viewport* vp, Viewer*v, input_event const *e)
01520 {
01521   size_t page_offset = cur_ - page_top_;
01522 
01523   page_top_ += vp->size().row_ - first_row_;
01524 
01525   if(page_top_ >= size())
01526     page_top_ = size() -1;
01527 
01528   if(page_top_ < 0)
01529     page_top_ = 0;
01530 
01531   cur_ = page_top_ + page_offset;
01532 
01533   if(cur_ >= size())
01534     cur_ = size() -1;
01535 
01536   if(cur_ < 0)
01537     cur_ = 0;
01538 
01539   paint_page(vp,v);
01540 
01541   return false;
01542 }
01543 
01546 
01547 AddMapping page_up_func(CursorWindow::func_prior, &Impl::page_up, NonModifying);
01548 
01549 bool
01550 Impl::
01551 page_up(viewport* vp, Viewer*v, input_event const *e)
01552 {
01553   size_t page_offset = cur_ - page_top_;
01554 
01555   if(page_top_ == 0)
01556   {
01557     cur_ = 0;
01558   }
01559   else
01560   {
01561     page_top_ -= vp->size().row_ - first_row_;
01562 
01563     if(page_top_ < 0)
01564       page_top_ = 0;
01565 
01566     cur_ = page_top_ + page_offset;
01567 
01568     if(cur_ < 0)
01569       cur_ = size() -1;
01570 
01571     if(cur_ < 0)
01572       cur_ = 0;
01573   }
01574 
01575   paint_page(vp,v);
01576 
01577   return false;
01578 }
01579 
01581 
01583 
01584 AddMapping tab_right_func(CursorWindow::func_tab, &Impl::tab_right, NonModifying);
01585 
01586 bool
01587 Impl::
01588 tab_right(viewport* vp, Viewer*v, input_event const *e)
01589 {
01590   if(cur_ >= size() || cur_ < 0)
01591     return false;
01592 
01593   col_ += htab_;
01594 
01595   make_col_visible(vp,v);
01596 
01597   return false;
01598 
01599 }
01600 
01602 
01604 
01605 AddMapping char_right_func(CursorWindow::func_right, &Impl::char_right, NonModifying);
01606 
01607 bool
01608 Impl::
01609 char_right(viewport* vp, Viewer*v, input_event const *e)
01610 {
01611   if(cur_ >= size() || cur_ < 0)
01612     return false;
01613 
01614   ++col_;
01615 
01616   make_col_visible(vp,v);
01617 
01618   return false;
01619 
01620 }
01621 
01622 void
01623 Impl::
01624 make_col_visible(viewport* vp, Viewer* v)
01625 {
01626   row_col size = vp->size();
01627 
01628   size_t  avail_cols = vwidth(size);
01629 
01630   if(col_ < page_left_)
01631   {
01632     // need to scroll left
01633 
01634      page_left_ = col_;
01635      page_left_ /= htab_;
01636      page_left_ *= htab_;
01637 
01638      paint_page(vp,v);
01639   }
01640   else
01641   if(col_ - page_left_ >= avail_cols)
01642   {
01643     // need to scroll right
01644 
01645     size_t target_col =  col_ / htab_;
01646            target_col += 1;
01647            target_col *= htab_;
01648         
01649     page_left_ = target_col - avail_cols;
01650 
01651     paint_page(vp,v);
01652 
01653   }
01654   else
01655   {
01656     restore_cursor(vp);
01657   }
01658 
01659 }
01660 
01662 
01664 
01665 AddMapping char_left_func(CursorWindow::func_left, &Impl::char_left, NonModifying);
01666 
01667 bool
01668 Impl::
01669 char_left(viewport* vp, Viewer*v, input_event const *e)
01670 {
01671   if(cur_ >= size() || cur_ < 0)
01672     return false;
01673 
01674   --col_;
01675 
01676   if( (int)(col_) < 0)
01677     col_ = 0;
01678 
01679   make_col_visible(vp,v);
01680 
01681   return false;
01682 
01683 }
01684 
01686 
01688 
01689 AddMapping line_down_func(CursorWindow::func_down, &Impl::line_down, NonModifying);
01690 
01691 bool
01692 Impl::
01693 line_down(viewport* vp, Viewer*v, input_event const *e)
01694 {
01695   ++cur_;
01696 
01697   make_line_visible(vp,v,false);
01698 
01699   return false;
01700 
01701 }
01702 
01703 void
01704 Impl::
01705 make_line_visible(viewport* vp, Viewer* v, bool center)
01706 {
01707   if(cur_ >= size())
01708     cur_ = size() - 1;
01709 
01710   if(cur_ < 0)
01711     cur_ = 0;
01712 
01713   row_col size = vp->size();
01714 
01715   int avail_rows = vheight(size);
01716 
01717   int row_offset = cur_ - page_top_;
01718 
01719   if(row_offset < 0)
01720   {
01721     if(center)
01722       page_top_ = cur_ - avail_rows/2;
01723     else
01724       page_top_ = cur_;
01725 
01726     if(page_top_ < 0)
01727       page_top_ = 0;
01728 
01729     paint_page(vp,v);
01730   }
01731   else
01732   if(row_offset >= avail_rows)
01733   {
01734     if(center)
01735       page_top_ = cur_ - (avail_rows/2 - 1);
01736     else
01737       page_top_ = cur_ - (avail_rows - 1);
01738 
01739     if(page_top_ < 0)
01740       page_top_ = 0;
01741 
01742     paint_page(vp,v);
01743 
01744   }
01745   else
01746   {
01747     restore_cursor(vp);
01748   }
01749 
01750 
01751 }
01752 
01753 
01755 
01757 
01758 AddMapping line_up_func(CursorWindow::func_up, &Impl::line_up, NonModifying);
01759 
01760 bool
01761 Impl::
01762 line_up(viewport* vp, Viewer*v, input_event const *e)
01763 {
01764   --cur_;
01765 
01766   make_line_visible(vp,v,false);
01767 
01768   return false;
01769 
01770 }
01771 
01773 
01775 
01776 AddMapping line_home_func(CursorWindow::func_home, &Impl::line_home, NonModifying);
01777 
01778 bool
01779 Impl::
01780 line_home(viewport* vp, Viewer*v, input_event const *e)
01781 {
01782   col_ = 0;
01783 
01784   make_col_visible(vp,v);
01785 
01786   return false;
01787 
01788 }
01789 
01791 
01793 
01794 AddMapping line_end_func(CursorWindow::func_end, &Impl::line_end, NonModifying);
01795 
01796 bool
01797 Impl::
01798 line_end(viewport* vp, Viewer*v, input_event const *e)
01799 {
01800 
01801   col_ = 0;
01802 
01803   if(size() > cur_)
01804     col_ = std::string(line_text(cur_)).size();
01805 
01806   make_col_visible(vp,v);
01807 
01808   return false;
01809 
01810 }
01811 
01813 
01815 
01816 AddMapping file_top_func(CursorWindow::func_top, &Impl::file_top, NonModifying);
01817 
01818 bool
01819 Impl::
01820 file_top(viewport* vp, Viewer*v, input_event const *e)
01821 {
01822 
01823   cur_ = 0;
01824 
01825   make_line_visible(vp,v);
01826 
01827   return false;
01828 
01829 }
01830 
01832 
01834 
01835 AddMapping file_bottom_func(CursorWindow::func_bottom, &Impl::file_bottom, NonModifying);
01836 
01837 bool
01838 Impl::
01839 file_bottom(viewport* vp, Viewer*v, input_event const *e)
01840 {
01841 
01842   cur_ = size()-1;
01843 
01844   if(cur_ < 0)
01845     cur_ = 0;
01846 
01847   make_line_visible(vp,v);
01848 
01849   return false;
01850 
01851 }
01852 
01854 
01855 
01857 
01858 AddMapping line_mark_func(CursorWindow::func_mark, &Impl::line_mark, NonModifying);
01859 
01860 bool
01861 Impl::
01862 line_mark(viewport* vp, Viewer*v, input_event const *e)
01863 {
01864   if(cur_ >= first_mark_ && cur_ < last_mark_ )
01865   {
01866     // clear extant mark
01867     first_mark_ = last_mark_ = -1;
01868   }
01869   else
01870   if(first_mark_ == last_mark_ )
01871   {
01872     // no previous mark:  mark this one line
01873 
01874     first_mark_ = cur_;
01875     last_mark_ = first_mark_ + 1;
01876   }
01877   else
01878   if(cur_ < first_mark_)
01879     first_mark_ = cur_;
01880   else
01881   if(cur_ >= last_mark_)
01882     last_mark_ = cur_ + 1;
01883 
01884   paint_page(vp,v);
01885 
01886   return false;
01887 }
01888 
01890 
01892 
01893 struct Accumulate_File_Lines
01894 : public FileName::accumulate_lines
01895   //
01901   //
01902 {
01903 
01904   rep_type &lines_;
01905 
01906   Accumulate_File_Lines(rep_type &lines)
01907   : lines_(lines)
01908   {
01909   }
01910 
01911   void push_back(std::string const &new_line)
01912   {
01913     lines_.push_back(new std::string(new_line));
01914   }
01915 
01916   std::string& back()
01917   {
01918     return *lines_.back().data();
01919   }
01920 
01921   void setBackHasCR(bool flag)
01922   {
01923      lines_.back().crRemoved_ = true; 
01924   }
01925 
01926 
01927 };
01928 
01929 void
01930 Impl::
01931 read_file()
01932 {
01933   //
01934   //  Read the file into the lines_ data structure
01935   //
01936 
01937   Accumulate_File_Lines accum(lines_);
01938 
01939   if(! fullname_.exists() )
01940   {
01941     manager_->window()->beep();
01942     is_new_ = true;
01943   }
01944   else
01945   {
01946 
01947 #ifdef _MSC_VER
01948     bool removeTrailingCROnUnix = false;
01949 #else
01950     bool removeTrailingCROnUnix = true;
01951 #endif
01952 
01953     
01954     std::string errors = fullname_.read_lines(accum,0,removeTrailingCROnUnix);
01955     
01956     if(errors.size() != 0)
01957     {
01958       CursorWindow::Message d("Error reading file " + fullname_);
01959     
01960       d += errors;
01961       d += "";
01962       d += "Press Enter to continue";
01963     
01964       d.popup(manager_->window());
01965     
01966       return;
01967     
01968     }
01969 
01970     is_writeable_ = fullname_.is_writeable();
01971 
01972   }
01973 
01974   std::string extension = fullname_.extension();
01975   std::string basename  = fullname_.basename();
01976 
01977   use_tabs_=true;
01978 
01979 
01980   if(extension == ".c"   ||
01981      extension == ".cc"  ||
01982      extension == ".h"   ||
01983      extension == ".cpp" ||
01984      extension == ".cxx" ||
01985      extension == ".C"   
01986     )
01987   {
01988     htab_     = 2;
01989     use_tabs_ = false;
01990   }
01991   else
01992   if(basename == "makefile" ||
01993      basename == "Makefile"
01994     )
01995   {
01996     htab_ = 8;
01997   }
01998   else
01999     htab_ = 4;
02000 
02001 
02002   // check for binary data
02003 
02004   if(lines_.size())
02005   {
02006       int count=0;
02007       bool hasBinary = false;
02008 
02009 
02010       for( ;   (!hasBinary)
02011             && (count < 10)
02012             && (count < lines_.size()); 
02013             ++count
02014          )
02015       {
02016 
02017           std::string const *p = lines_[count].data();
02018 
02019           if(!p)
02020             continue;
02021           
02022 
02023           std::string::const_iterator l=p->begin(),
02024                                       g=p->end();
02025 
02026           while(   !hasBinary 
02027                 && (l != g)
02028                )
02029           {
02030             unsigned char c = *l++;
02031 
02032             if( c > '~')
02033             {
02034                hasBinary=true;
02035                break;
02036             }
02037 
02038             if(c < ' ')
02039             {
02040                if(    (c != '\r')
02041                   &&  (c != '\n')
02042                   &&  (c != 0x09)  // tab
02043                  )
02044                {
02045                    hasBinary = true;
02046                    break;
02047                }
02048             }
02049 
02050           }
02051 
02052       }  // processing characters in first 10 lines of the fil
02053 
02054       if(hasBinary)
02055       {
02056          manager_->window()->beep();
02057 
02058         CursorWindow::Message d("Warning:  Binary data");
02059 
02060         d += "";
02061         d += "";
02062         d += "Warning: this file contains binary data.";
02063         d += "";
02064         d += "This editor is not meant to be used with binary data.";
02065         d += "Saving changes is disabled to prevent data destruction.";
02066         d += "";
02067         d += "You can use ^Z to re-enable writing to the file, but do";
02068         d += "so at your own risk.";
02069         d += "";
02070 
02071         
02072         d.popup(manager_->window());
02073         is_writeable_ = false;
02074 
02075          
02076       }
02077 
02078   }  // check for binary
02079 
02080 
02081 
02082 }
02084 
02086 AddMapping word_next_func(CursorWindow::func_nextwd, &Impl::word_next, NonModifying);
02087 
02088 bool
02089 Impl::
02090 word_next(viewport* vp, Viewer*v, input_event const *e)
02091 {
02092   std::string line = line_text(cur_);
02093 
02094   std::string::iterator first = line.begin(),
02095                         last  = line.end();
02096 
02097   if(col_ >= line.size())
02098   {
02099 
02100     if(cur_ < size())
02101     {
02102       ++cur_;
02103       col_ = 0;
02104       make_line_visible(vp,v);
02105     }
02106   }
02107                         
02108   if(cur_ < size() && col_ < line.size())
02109   {
02110     // not off the end of the line
02111 
02112     std::string::iterator start = first + col_;
02113 
02114     std::string::iterator next_word_start = StrTool::find_next_word(start,
02115                                                                     last,
02116                                                                     word_chars_.begin(),
02117                                                                     word_chars_.end()
02118                                                                    );
02119    if(next_word_start != last)
02120    {
02121       col_  += next_word_start - start;
02122 
02123    }
02124    else
02125    {
02126       col_ = line.size();
02127    }
02128 
02129    make_col_visible(vp,v);
02130   }
02131 
02132   return false;
02133 }
02135 
02136 
02138 AddMapping word_prev_func(CursorWindow::func_prevwd, &Impl::word_prev, NonModifying);
02139 
02140 bool
02141 Impl::
02142 word_prev(viewport* vp, Viewer*v, input_event const *e)
02143 {
02144   std::string line = line_text(cur_);
02145 
02146   std::string::iterator first = line.begin(),
02147                         last  = line.end();
02148 
02149   if(col_)
02150   {
02151     std::string::iterator start = first + col_;
02152 
02153     std::string::iterator word_begin = StrTool::find_prev_word(start,
02154                                                                first,
02155                                                                last,
02156                                                                word_chars_.begin(),
02157                                                                word_chars_.end()
02158                                                               );
02159     col_ = word_begin - first;
02160 
02161     make_col_visible(vp,v);
02162 
02163 
02164   }
02165   else
02166   {
02167     if(cur_)
02168     {
02169       // move back to the end of the previous line
02170 
02171       --cur_;
02172 
02173       line = line_text(cur_);
02174 
02175       col_ = line.size();
02176 
02177       make_line_visible(vp,v);
02178 
02179     }
02180   }
02181 
02182   return false;
02183 }
02185 
02186 
02188 AddMapping word_del_func(CursorWindow::func_dw, &Impl::word_delete, Modifying);
02189 
02190 bool
02191 Impl::
02192 word_delete(viewport* vp, Viewer*v, input_event const *e)
02193 {
02194   std::string line = line_text(cur_);
02195 
02196   if(col_ >= line.size())
02197   {
02198 
02199     if(cur_ < size())
02200     {
02201       ++cur_;
02202       col_ = 0;
02203       line = line_text(cur_);
02204       make_line_visible(vp,v);
02205     }
02206   }
02207 
02208   std::string::iterator first = line.begin(),
02209                         last  = line.end();
02210 
02211 
02212                         
02213   if(cur_ < size() && col_ < line.size())
02214   {
02215     // not off the end of the line
02216 
02217     std::string::iterator start = first + col_;
02218 
02219     std::string::iterator next_word_start = StrTool::find_next_word(start,
02220                                                                     last,
02221                                                                     word_chars_.begin(),
02222                                                                     word_chars_.end()
02223                                                                    );
02224     size_t bytes_till_next_word;
02225                                                                 
02226     if(next_word_start != last)
02227     {
02228        bytes_till_next_word = next_word_start - start;
02229 
02230     }
02231     else
02232     {
02233        bytes_till_next_word = line.size() - col_;;
02234     }
02235 
02236 
02237     last = first + ( col_ + bytes_till_next_word );
02238 
02239     line.erase(start,last);
02240 
02241     replace_line(cur_, line);
02242 
02243     row_col curpos = vp->curpos();
02244 
02245     make_col_visible(vp,v);
02246 
02247     vp->set_curpos(curpos.row_, left_);
02248 
02249     display_line(vp, cur_);
02250 
02251   }
02252 
02253   restore_cursor(vp);
02254 
02255   return false;
02256 }
02258 
02259 
02261 AddMapping char_insert_func(CursorWindow::func_data, &Impl::char_insert, Modifying);
02262 
02263 bool
02264 Impl::
02265 char_insert(viewport* vp, Viewer*v, input_event const *e)
02266 {
02267   if(cur_ >= size())
02268   {
02269     if(cur_ == 0)
02270     {
02271       lines_.push_back(new std::string(""));
02272       paint_page(vp,v);
02273       restore_cursor(vp);
02274     }
02275     else
02276       return false;  // something is wrong with the data structures
02277   }
02278 
02279 
02280   std::string line = line_text(cur_);
02281 
02282   std::string::iterator first = line.begin();
02283 
02284   if(col_ <= line.size())
02285   {
02286     line.insert(first+col_, e->value_);
02287   }
02288   else
02289   {
02290      line.append(col_ - line.size(), ' ');
02291      line += (char)(e->value_);
02292   }
02293 
02294   ++col_;
02295 
02296   replace_line(cur_, line);
02297 
02298   row_col curpos = vp->curpos();
02299 
02300   make_col_visible(vp,v);
02301 
02302   vp->set_curpos(curpos.row_, left_);
02303 
02304   display_line(vp, cur_);
02305 
02306   restore_cursor(vp);
02307 
02308   return false;
02309 }
02311 
02312 
02314 AddMapping char_delete_func(CursorWindow::func_dc, &Impl::char_delete, Modifying);
02315 
02316 bool
02317 Impl::
02318 char_delete(viewport* vp, Viewer*v, input_event const *e)
02319 {
02320   if(cur_ >= size())
02321     return false;
02322 
02323 
02324   std::string line = line_text(cur_);
02325 
02326   if(col_ >= line.size())
02327   {
02328     line_join(vp,v,e);
02329 
02330     line = line_text(cur_);
02331 
02332     col_ = line.size();
02333 
02334     make_col_visible(vp,v);
02335 
02336     return false;
02337 
02338   }
02339 
02340 
02341   std::string::iterator first = line.begin();
02342 
02343   if(col_ < line.size())
02344   {
02345     line.erase(first+col_);
02346   }
02347 
02348   replace_line(cur_,line);
02349 
02350   row_col curpos = vp->curpos();
02351 
02352   make_col_visible(vp,v);
02353 
02354   vp->set_curpos(curpos.row_, left_);
02355 
02356   display_line(vp, cur_);
02357 
02358   restore_cursor(vp);
02359 
02360   return false;
02361 }
02363 
02365 
02366 AddMapping line_del_func(CursorWindow::func_dl, &Impl::line_del, Modifying);
02367 
02368 bool
02369 Impl::
02370 line_del(viewport* vp, Viewer*v, input_event const *e)
02371 {
02372   if(cur_ >= size())
02373     return false;
02374 
02375   // erase the extant paste buffer
02376 
02377   Viewer::paste_buffer_type &paste_buffer = *v->paste_buffer();
02378 
02379   paste_buffer.erase(paste_buffer.begin(), paste_buffer.end());
02380 
02381   paste_buffer.insert(paste_buffer.begin(), *line_text(cur_).data() );
02382 
02383   v->commitPasteBuffer();
02384 
02385   erase_line( line_address(cur_), cur_ );
02386 
02387   if(cur_ >= size())
02388     cur_ = size() -1;
02389 
02390   if(cur_ < 0)
02391     cur_ = 0;
02392 
02393   if(first_mark_ != last_mark_)
02394   {
02395     //
02396     // handle adjustments to the marked region
02397     //
02398     if(cur_ >= first_mark_ && cur_ < last_mark_)
02399     {
02400       // delete a line in the marked region
02401 
02402       if(cur_ == first_mark_)
02403         ++first_mark_;
02404       else
02405         --last_mark_;
02406     }
02407     else
02408     if(cur_ < first_mark_)
02409     {
02410       --first_mark_;
02411       --last_mark_;
02412     }
02413   }
02414 
02415   paint_page(vp,v);
02416 
02417   return false;
02418 }
02420 
02421 
02423 
02424 AddMapping line_split_func(CursorWindow::func_enter, &Impl::line_split, Modifying);
02425 
02426 bool
02427 Impl::
02428 line_split(viewport* vp, Viewer*v, input_event const *e)
02429 {
02430   if(cur_ >= size())
02431   {
02432     // no lines defined yet
02433 
02434     lines_.push_back( new std::string("") );
02435     col_ = 0;
02436     paint_page(vp,v);
02437     return false;
02438   }
02439 
02440   std::string line = line_text(cur_);
02441 
02442   size_t first_non_blank_col=0;
02443 
02444   while(first_non_blank_col < line.size())
02445   {
02446     if(line[first_non_blank_col] != ' ')
02447       break;
02448 
02449     ++first_non_blank_col;
02450   }
02451 
02452   if(first_non_blank_col >= line.size())
02453     first_non_blank_col = 0;
02454   else
02455     left_margin_ = first_non_blank_col;
02456 
02457   if(col_ >= line.size())
02458   {
02459     // already past the end of line, just insert a new one
02460 
02461     rep_type::iterator cur_line_ptr = lines_.find(cur_);
02462 
02463     ++cur_line_ptr;
02464 
02465     lines_.insert(cur_line_ptr, new std::string(""));
02466 
02467     ++cur_;
02468     col_ = left_margin_;
02469     make_col_visible(vp,v);
02470     make_line_visible(vp,v);
02471 
02472     paint_page(vp,v);
02473 
02474     UndoOperation* op = push_undo_stack();
02475 
02476     op->code_    = UndoOperation::erase_line;
02477     op->lineno_  = cur_;
02478 
02479 
02480     return false;
02481 
02482   }
02483 
02484   // ok, add a new line which is the tail of the current line
02485   // and rip the tail off the current line
02486 
02487 
02488   rep_type::iterator cur_line_ptr = lines_.find(cur_);
02489 
02490   ++cur_line_ptr;
02491 
02492   std::string new_line = line;
02493 
02494   new_line.erase(new_line.begin(), new_line.begin() + col_);
02495 
02496   StrTool::remove_leading(new_line,' ');
02497 
02498   {
02499     UndoOperation *op = push_undo_stack();
02500 
02501 
02502     op->code_ = UndoOperation::join_lines;
02503     op->lineno_ = cur_;
02504     op->text_ = line;
02505 
02506   }
02507 
02508 
02509   std::string line_to_insert(left_margin_, ' ');
02510               line_to_insert += std::string(new_line);
02511 
02512   lines_.insert(cur_line_ptr, new std::string(line_to_insert) );
02513 
02514   line.erase(line.begin() + col_, line.end());
02515 
02516   replace_line(cur_,line,false);
02517 
02518   ++cur_;
02519   col_ = left_margin_;
02520 
02521   make_line_visible(vp,v);
02522   paint_page(vp,v);
02523 
02524 
02525   return false;
02526 }
02528 
02530 
02531 AddMapping line_clreol_func(CursorWindow::func_clreol, &Impl::line_clreol, Modifying);
02532 
02533 bool
02534 Impl::
02535 line_clreol(viewport* vp, Viewer*v, input_event const *e)
02536 {
02537   if(cur_ >= size())
02538     return false;
02539 
02540   std::string line = line_text(cur_);
02541 
02542   std::string::iterator first = line.begin();
02543 
02544   if(col_ < line.size())
02545   {
02546     line.erase(first+col_, line.end());
02547   }
02548 
02549   replace_line(cur_,line);
02550 
02551   row_col curpos = vp->curpos();
02552 
02553   make_col_visible(vp,v);
02554 
02555   vp->set_curpos(curpos.row_, left_);
02556 
02557   display_line(vp, cur_);
02558 
02559   restore_cursor(vp);
02560 
02561   return false;
02562 }
02564 
02565 
02567 AddMapping char_delprv_func(CursorWindow::func_dc_prev, &Impl::char_delprv, Modifying);
02568 
02569 bool
02570 Impl::
02571 char_delprv(viewport* vp, Viewer*v, input_event const *e)
02572 {
02573   if(cur_ >= size())
02574     return false;
02575 
02576   std::string line = line_text(cur_);
02577 
02578   if(col_ == 0)
02579   {
02580     // join this line to the previous line
02581 
02582     if(cur_)
02583     {
02584       // if not at top of the file
02585 
02586       std::string prevline = line_text(cur_-1);
02587 
02588       size_t prevlength = prevline.size();
02589 
02590       prevline += line;
02591 
02592       replace_line(cur_-1,prevline);
02593 
02594       erase_line(line_address(cur_), cur_);
02595 
02596       --cur_;
02597 
02598       col_ = prevlength;
02599 
02600       make_col_visible(vp,v);
02601       make_line_visible(vp,v);
02602       paint_page(vp,v);
02603 
02604 
02605     }
02606 
02607     return false;
02608   }
02609   else
02610   if(col_ > line.size())
02611   {
02612     // just move the cursor back one character
02613 
02614     --col_;
02615     make_col_visible(vp,v);
02616     restore_cursor(vp);
02617     return false;
02618   }
02619 
02620   // ok, delete the previous character and leave the cursor
02621   // in tht position
02622 
02623   --col_;
02624   make_col_visible(vp,v);
02625 
02626   return char_delete(vp,v,e);
02627 
02628 }
02630 
02632 AddMapping find_func(CursorWindow::func_find, &Impl::find_first, NonModifying);
02633 
02634 bool
02635 Impl::
02636 find_first(viewport* vp, Viewer*v, input_event const *e)
02637 {
02638   CursorWindow::Dialog dialog("Find string");
02639 
02640   char const *mix = "Y";
02641 
02642   if(mixed_case_ == false)
02643     mix = "N";
02644 
02645   dialog += new CursorWindow::Dialog::String("case",
02646                                              "Mixed case",
02647                                              mix,
02648                                              2
02649                                             );
02650 
02651 
02652   dialog += new CursorWindow::Dialog::String("mode",
02653                                              "(W)ord, (R)egexp, or (S)tring",
02654                                              (is_regex_ ? "R" : (match_words_ ? "W" : "S")),
02655                                              2
02656                                             );
02657 
02658   dialog += new CursorWindow::Dialog::String("text",
02659                                              "Search for",
02660                                              search_text_,
02661                                              40
02662                                             );
02663 
02664   dialog += new CursorWindow::Dialog::Ok("ok","");
02665 
02666   dialog.set_first_input_field("text");
02667 
02668 
02669   if( dialog.popup(vp->window()) )
02670     return false;
02671 
02672   std::string mixed_case = dialog.element_value("case");
02673 
02674   if(   mixed_case.size() 
02675      && (mixed_case[0] == 'n' || mixed_case[0] == 'N')
02676     )
02677   {
02678     mixed_case_ = false;
02679   }
02680   else
02681   if(   mixed_case.size() 
02682      && (mixed_case[0] == 'y' || mixed_case[0] == 'Y')
02683     )
02684   {
02685     mixed_case_ = true;
02686   }
02687 
02688 
02689   std::string match_words = dialog.element_value("mode");
02690 
02691   match_words_ = false;
02692   is_regex_    = false;
02693 
02694   if(   match_words.size() 
02695      && (match_words[0] == 'r' || match_words[0] == 'R')
02696     )
02697   {
02698     is_regex_ =  true;
02699   }
02700   else
02701   if(   match_words.size() 
02702      && (match_words[0] == 'w' || mixed_case[0] == 'W')
02703     )
02704   {
02705     match_words_ = true;
02706   }
02707 
02708   std::string search_text = dialog.element_value("text");
02709 
02710   if(search_text.size() == 0)
02711     return false; // ignore searches for nothing
02712 
02713   search_text_ = search_text;
02714 
02715   int    saved_line   = cur_;
02716   size_t saved_col    = col_;
02717 
02718   --col_;  // first time through, let it try the current column
02719            // too.  find next always skips the current character
02720 
02721   if(is_regex_ && search_text_[0] == '^')
02722     --cur_;  // find next will increment it
02723 
02724   if(!find_next_helper(vp,v,e))
02725   {
02726     cur_ = saved_line;
02727     col_ = saved_col;
02728     vp->beep();
02729   }
02730   else
02731   {
02732     make_col_visible(vp,v);
02733     make_line_visible(vp,v);
02734   }
02735 
02736   return false;
02737 }
02739 
02741 AddMapping find_func_prev(CursorWindow::func_findprv, &Impl::find_prev, NonModifying);
02742 
02743 bool
02744 Impl::
02745 find_prev(viewport* vp, Viewer*v, input_event const *e)
02746 {
02747   int    saved_line   = cur_;
02748   size_t saved_col    = col_;
02749 
02750   if(!find_prev_helper(vp,v,e))
02751   {
02752     cur_ = saved_line;
02753     col_ = saved_col;
02754     vp->beep();
02755   }
02756   else
02757   {
02758     make_col_visible(vp,v);
02759     make_line_visible(vp,v);
02760   }
02761 
02762   return false;
02763 }
02764 
02765 bool
02766 Impl::
02767 find_prev_helper(viewport* vp, Viewer*v, input_event const *e)
02769 {
02770   // the caller must save/restore cur_ and col_
02771 
02772   --col_;
02773 
02774 
02775   if(is_regex_)
02776   {
02777     SimpleRegex *p = &regexp_;
02778 
02779     p->SimpleRegex::~SimpleRegex();  // destruct current regex
02780 
02781     new(&regexp_) SimpleRegex(search_text_,  (mixed_case_ ? "i" : "") );
02782 
02783     //    regexp_ = search_text_;
02784 
02785     if(search_text_[0] == '^')
02786     {
02787       ++cur_;
02788       col_ = 0;
02789     }
02790   }
02791   
02792   
02793 
02794 
02795   bool force_col_to_end = false;
02796 
02797   while(cur_ >= 0)
02798   { // search all lines till beginning of file
02799 
02800     // get the text of the current line, tab expanded
02801 
02802     std::string curline = line_text(cur_);
02803 
02804     // make sure the line isn't empty and that the cursor
02805     // variable is properly positioned
02806 
02807     if(force_col_to_end)
02808     {
02809       col_ = curline.size()-1;
02810 
02811       if(col_ < 0)
02812       {
02813         --cur_;
02814         continue;
02815       }
02816     }
02817 
02818     // since backwards searches are hard, lets use a forward search
02819     // and make a list of all matches, as column numbers in this line,
02820     // then search around in that list for column numbers
02821 
02822     std::vector<int> matching_columns;
02823 
02824     for(int scan = 0; size_t(scan) < curline.size(); )
02825     {
02826 
02827       std::string::iterator where;
02828 
02829 
02830       if(is_regex_)
02831       {
02832         int startpos = scan;
02833         
02834         if(search_text_[0] == '^')
02835           startpos = 0;
02836 
02837         int count = regexp_(curline, startpos, curline.size());
02838         
02839         if(count > 0)
02840         {
02841           where = (curline.begin() + startpos + regexp_.matches()[0].offset);
02842         }
02843         else
02844           where = curline.end();
02845         
02846       }
02847       else
02848       {
02849         where = std::search(curline.begin() + scan,
02850                             curline.end(),
02851                             search_text_.begin(),
02852                             search_text_.end(),
02853                             CharCompare(this)
02854                            );
02855       }
02856 
02857                                         
02858       if(where != curline.end())
02859       {
02860         // found a match
02861         
02862         scan = where - curline.begin();
02863         
02864         matching_columns.push_back( scan );
02865         
02866         ++scan;
02867       }
02868       else
02869       {
02870         break;
02871       }
02872     }
02873 
02874     // matching_columns now has a list of all matches on this line
02875 
02876 
02877     if( int(col_) >= 0 )
02878     {
02879       // not past beginning of current line -- so try a search here
02880 
02881       // scan across the list of matches, until the current match
02882       // is farther to the right than the current column
02883 
02884       int match;
02885 
02886       for(match = 0; size_t(match) < matching_columns.size(); ++match)
02887       {
02888         if(matching_columns[match] > int(col_) )
02889           break;
02890       }
02891 
02892       --match;
02893 
02894       if(match >= 0)
02895       {
02896           std::string::iterator where = curline.begin() + matching_columns[match];
02897         
02898          if(match_words_)
02899          {
02900            // if matching only whole words, check to see if this is in fact a word
02901            // and if not, don't stop searching
02902         
02903            bool begin_starts_word = (where == curline.begin());
02904         
02905            if(!begin_starts_word)
02906              begin_starts_word = !is_word_char(where[-1]);
02907         
02908            std::string::iterator word_end = where + search_text_.size();
02909         
02910            bool end_ends_word = (word_end == curline.end());
02911         
02912            if(!end_ends_word)
02913              end_ends_word = !is_word_char(*word_end);
02914         
02915            if(begin_starts_word && end_ends_word)
02916            {
02917              col_ = where - curline.begin();
02918              return true;
02919            }
02920         
02921            col_ = where - curline.begin();
02922            --col_;
02923            force_col_to_end = false;
02924         
02925            continue;
02926         
02927         
02928          }
02929          else
02930          {
02931            // any old match will do, so quit
02932            col_ = where - curline.begin();
02933            return true;
02934          }
02935       }
02936     }
02937 
02938     --cur_;
02939 
02940     force_col_to_end=true;
02941 
02942   }
02943 
02944 
02945   return false;
02946 
02947 }
02949 
02951 AddMapping find_func_next(CursorWindow::func_findnxt, &Impl::find_next, NonModifying);
02952 
02953 bool
02954 Impl::
02955 find_next(viewport* vp, Viewer*v, input_event const *e)
02956 {
02957   int    saved_line   = cur_;
02958   size_t saved_col    = col_;
02959 
02960   if(!find_next_helper(vp,v,e))
02961   {
02962     cur_ = saved_line;
02963     col_ = saved_col;
02964     vp->beep();
02965   }
02966   else
02967   {
02968     make_col_visible(vp,v);
02969     make_line_visible(vp,v);
02970   }
02971 
02972   return false;
02973 }
02974 
02975 
02976 
02977 bool
02978 Impl::
02979 find_next_helper(viewport* vp, Viewer*v, input_event const *e)
02981 {
02982   // the caller must save/restore cur_ and col_
02983 
02984   ++col_;  // note that replace_next() decrements this prior to the call to
02985            // replace_next_helper() which calls this function.
02986 
02987   if(is_regex_)
02988   {
02989     SimpleRegex *p = &regexp_;
02990 
02991     p->SimpleRegex::~SimpleRegex();  // destruct current regex
02992 
02993     new(&regexp_) SimpleRegex(search_text_,  (mixed_case_ ? "i" : "") );
02994 
02995     //    regexp_ = search_text_;
02996 
02997     if(search_text_[0] == '^')
02998     {
02999       ++cur_;
03000       col_ = 0;
03001     }
03002   }
03003 
03004 
03005   while(cur_ < lines_.size())
03006   { // search all lines till eof
03007 
03008     std::string curline = line_text(cur_);
03009 
03010     if(col_ < curline.size())
03011     {
03012 
03013       int startpos = col_;
03014 
03015       // not past end of current line -- so try a search here
03016 
03017       std::string::iterator where;
03018 
03019 
03020       if(is_regex_)
03021       {
03022         
03023         if(search_text_[0] == '^')
03024           startpos = 0;
03025 
03026         int count = regexp_(curline, startpos, curline.size());
03027         
03028         if(count > 0)
03029         {
03030           where = (curline.begin() + regexp_.matches()[0].offset);
03031         }
03032         else
03033           where = curline.end();
03034         
03035       }
03036       else
03037       {
03038         where = std::search(curline.begin() + col_,
03039                                                   curline.end(),
03040                                                   search_text_.begin(),
03041                                                   search_text_.end(),
03042                                                   CharCompare(this)
03043                                                  );
03044       }
03045                                         
03046       if(where != curline.end())
03047       {
03048         
03049          if(match_words_ && !is_regex_)
03050          {
03051            // if matching only whole words, check to see if this is in fact a word
03052            // and if not, don't stop searching
03053         
03054            bool begin_starts_word = (where == curline.begin());
03055         
03056            if(!begin_starts_word)
03057              begin_starts_word = !is_word_char(where[-1]);
03058         
03059            std::string::iterator word_end = where + search_text_.size();
03060         
03061            bool end_ends_word = (word_end == curline.end());
03062         
03063            if(!end_ends_word)
03064              end_ends_word = !is_word_char(*word_end);
03065         
03066            if(begin_starts_word && end_ends_word)
03067            {
03068              col_ = where - curline.begin();
03069              return true;
03070            }
03071         
03072            col_ = where - curline.begin();
03073            ++col_;
03074         
03075            continue;
03076         
03077         
03078          }
03079          else
03080          {
03081            // any old match will do, so quit
03082            col_ = where - curline.begin();
03083 
03084 
03085 
03086            return true;
03087          }
03088       }
03089     }
03090 
03091     ++cur_;
03092     col_ = 0;
03093 
03094   }
03095 
03096 
03097   return false;
03098 
03099 }
03100 
03102 
03103 
03104 
03105 
03107 
03108 AddMapping replace_first_func(CursorWindow::func_repl, &Impl::replace_first, Modifying);
03109 
03110 bool
03111 Impl::
03112 replace_first(viewport* vp, Viewer*v, input_event const *e)
03113   // Prompt the user for a find/replacement pair and replace the first
03114   // match.
03115 {
03116   CursorWindow::Dialog dialog("Replace string");
03117 
03118 
03119   char const *mix = "Y";
03120 
03121   if(mixed_case_ == false)
03122     mix = "N";
03123 
03124   dialog += new CursorWindow::Dialog::String("case",
03125                                              "Mixed case",
03126                                              mix,
03127                                              2
03128                                             );
03129 
03130   dialog += new CursorWindow::Dialog::String("mode",
03131                                              "(W)ord, (R)egexp, or (S)tring",
03132                                              (is_regex_ ? "R" : (match_words_ ? "W" : "S")),
03133                                              2
03134                                             );
03135 
03136   dialog += new CursorWindow::Dialog::String("all", "Replace (a)ll or (n)ext",
03137                                              "n",
03138                                              2
03139                                             );
03140 
03141   dialog += new CursorWindow::Dialog::String("find",
03142                                              "Search for",
03143                                              search_text_,
03144                                              40
03145                                             );
03146                                         
03147   dialog += new CursorWindow::Dialog::String("replacement",
03148                                              "Replace with",
03149                                              replacement_,
03150                                              40
03151                                             );
03152                                         
03153 
03154   dialog += new CursorWindow::Dialog::Ok("ok","");
03155 
03156   dialog.set_first_input_field("find");
03157 
03158   if( dialog.popup(vp->window()) )
03159     return false;
03160 
03161   std::string mixed_case = dialog.element_value("case");
03162 
03163   if(   mixed_case.size() 
03164      && (mixed_case[0] == 'n' || mixed_case[0] == 'N')
03165     )
03166   {
03167     mixed_case_ = false;
03168   }
03169   else
03170   if(   mixed_case.size() 
03171      && (mixed_case[0] == 'y' || mixed_case[0] == 'Y')
03172     )
03173   {
03174     mixed_case_ = true;
03175   }
03176 
03177   std::string match_words = dialog.element_value("mode");
03178 
03179   match_words_ = false;
03180   is_regex_    = false;
03181 
03182   if(   match_words.size() 
03183      && (match_words[0] == 'r' || match_words[0] == 'R')
03184     )
03185   {
03186     is_regex_ =  true;
03187   }
03188   else
03189   if(   match_words.size() 
03190      && (match_words[0] == 'w' || mixed_case[0] == 'W')
03191     )
03192   {
03193     match_words_ = true;
03194   }
03195 
03196 
03197   std::string search_text = dialog.element_value("find");
03198   std::string replacement = dialog.element_value("replacement");
03199 
03200   if(search_text.size() == 0)
03201     return false;
03202 
03203   search_text_ = search_text;
03204   replacement_ = replacement;
03205 
03206   int    saved_line = cur_;
03207   size_t saved_col  = col_;
03208 
03209 
03210   if(is_regex_ && search_text_[0] == '^')
03211     --cur_;  // replace_next_helper, through find next helper, will increment it
03212 
03213   if( tolower(dialog.element_value("all")[0]) == 'a')
03214   {
03215     // replace all matches
03216 
03217     UndoOperation::undo_stack* nested_changes = new UndoOperation::undo_stack;
03218 
03219     undo_stack_.swap(*nested_changes);  // temporarily swap the real undo
03220                                         // stack withe the new one just
03221                                         // created so we can then place the
03222                                         // changes caused by replace_next_helper
03223                                         // to be accumulated into the new
03224                                         // stack -- which will then be placed on
03225                                         // the real undo stack
03226 
03227     bool found_one=false;
03228 
03229     for(;;)
03230     {
03231         --col_;  // will be incremented immediately by replace_next_helper()
03232                  // through it's call to find_next_helper()
03233 
03234         if(!replace_next_helper(vp,v,e))
03235            break;
03236 
03237       found_one = true;
03238     }
03239 
03240     undo_stack_.swap(*nested_changes);  // put the old undo stack back
03241 
03242 
03243     UndoOperation* op = push_undo_stack();
03244 
03245     op->code_ = UndoOperation::nested_undo_ops;
03246 
03247     op->nested_undo_stack_ = nested_changes;
03248 
03249     op->nested_first_mark_ = first_mark_;
03250     op->nested_last_mark_  = last_mark_;
03251 
03252     if(!found_one)
03253     {
03254       vp->beep();
03255       cur_ = saved_line;
03256       col_ = saved_col;
03257     }
03258     else
03259     {
03260       // cursor left at end of replacement
03261     
03262       make_col_visible(vp,v);
03263       make_line_visible(vp,v);
03264       paint_page(vp,v);
03265 
03266     }
03267 
03268   }
03269   else
03270   {
03271     // replace only the next match
03272 
03273   --col_;  // replace_next_helper will increment col_
03274            // immediately through it's call to find_next_helper
03275 
03276     if(replace_next_helper(vp,v,e))
03277     {
03278       // cursor left at end of replacement
03279     
03280       make_col_visible(vp,v);
03281       make_line_visible(vp,v);
03282       paint_page(vp,v);
03283     
03284     }
03285     else
03286     {
03287       cur_ = saved_line;
03288       col_ = saved_col;
03289       vp->beep();
03290     }
03291   }
03292 
03293 
03294   return false;
03295 }
03296 
03297 bool
03298 Impl::
03299 replace_next_helper(viewport* vp, Viewer*v, input_event const *e)
03303 {
03304 
03305   // note:  replace_next() decrements col_ before calling this function
03306   //        and find_next_helper increments it before doing anything else
03307 
03308   if(!find_next_helper(vp,v,e))
03309   {
03310     return false;
03311   }
03312 
03313   if(is_regex_)
03314   {
03315     std::string curline = line_text(cur_);
03316 
03317     std::string replacement = regexp_.substitute(curline, replacement_);
03318 
03319     replace_line(cur_,replacement);
03320 
03321     col_ += regexp_.matches()[0].length;
03322 
03323     col_ += replacement.size() - curline.size();
03324 
03325   }
03326   else
03327   {
03328 
03329     // found it, cursor is at the beginning of the word
03330 
03331     std::string curline = line_text(cur_);
03332 
03333     std::string::iterator target = curline.begin() + col_;
03334 
03335     curline.erase(target, target + search_text_.size());
03336 
03337     curline.insert(col_, replacement_);
03338 
03339     replace_line(cur_,curline);
03340 
03341     col_ += replacement_.size();
03342 
03343   }
03344 
03345   return true;
03346 
03347 }
03348 
03349 
03351 
03353 AddMapping replace_next_func(CursorWindow::func_replnext, &Impl::replace_next, Modifying);
03354 
03355 bool
03356 Impl::
03357 replace_next(viewport* vp, Viewer*v, input_event const *e)
03358 {
03359 
03360   int    saved_line   = cur_;
03361   size_t saved_col    = col_;
03362 
03363 
03364   --col_;  // it will be incremented immediately find_next_helper which is called
03365            // the first thing in find_next_helper
03366 
03367   if(!replace_next_helper(vp,v,e))
03368   {
03369     cur_ = saved_line;
03370     col_ = saved_col;
03371     vp->beep();
03372   }
03373   else
03374   {
03375 
03376     make_col_visible(vp,v);
03377     make_line_visible(vp,v);
03378 
03379     vp->set_curpos(first_row_ + cur_ - page_top_, left_);
03380     display_line(vp, cur_);
03381 
03382     restore_cursor(vp);
03383 
03384   }
03385 
03386   return false;
03387 }
03388 
03390 
03392 AddMapping delete_block_func(CursorWindow::func_db, &Impl::block_delete, Modifying);
03393 
03394 bool
03395 Impl::
03396 block_delete(viewport* vp, Viewer*v, input_event const *e)
03397   // cut the marked block to the paste buffer
03398 {
03399   if(first_mark_ == last_mark_)
03400   {
03401     vp->beep();
03402     return false;  // no marked block
03403   }
03404 
03405 
03406   // erase the extant paste buffer, fill it with the deleted
03407   // items
03408 
03409   Viewer::paste_buffer_type &paste_buffer = *v->paste_buffer();
03410 
03411   paste_buffer.erase(paste_buffer.begin(), paste_buffer.end());
03412 
03413   // Create an undo operation to hold the deleted lines
03414 
03415   UndoOperation *op = push_undo_stack();
03416 
03417   op->code_   = UndoOperation::insert_block;
03418   op->lineno_ = first_mark_;
03419 
03420   // delete the lines and copy them to the paste
03421   // buffer
03422 
03423   while(last_mark_ != first_mark_)
03424   {
03425     // delete marked lines starting at tail to simplify work
03426 
03427     --last_mark_;
03428 
03429     rep_type::iterator scan = line_address(last_mark_);
03430 
03431     if(scan != lines_.end())
03432     {
03433       EditLine &el =*scan;
03434 
03435       paste_buffer.insert(paste_buffer.begin(), el);
03436       op->block_.push_back(el);
03437 
03438       erase_line(scan, last_mark_, false);  // erase but don't save undo info
03439                                             // since we are doing it already
03440     }
03441 
03442   }
03443 
03444   cur_ = first_mark_;
03445   col_ = 0;
03446   page_left_ = 0;
03447   page_top_  = first_mark_;
03448 
03449   first_mark_ = last_mark_ = -1;
03450 
03451   paint_page(vp, v);
03452 
03453   v->commitPasteBuffer();
03454 
03455 
03456   return false;
03457 }
03459 
03461 AddMapping paste_block_func(CursorWindow::func_paste, &Impl::block_paste, Modifying);
03462 
03463 bool
03464 Impl::
03465 block_paste(viewport* vp, Viewer*v, input_event const *e)
03466   // cut the marked block to the paste buffer
03467 {
03468   Viewer::paste_buffer_type &paste_buffer = *v->paste_buffer();
03469 
03470   Viewer::paste_buffer_type::iterator first = paste_buffer.begin(),
03471                                       last  = paste_buffer.end();
03472 
03473 
03474   UndoOperation* op = push_undo_stack();
03475 
03476   op->code_ = UndoOperation::delete_block;
03477   op->count_= 0;
03478   op->lineno_ = cur_;
03479                                 
03480   while(first != last)
03481   {
03482     std::string const *insertedLine = &( *first++ );
03483 
03484     std::string buffer;
03485 
03486     if(use_tabs_)
03487     {
03488        // if we are going to save this file with tabs, then we should 
03489        // tab compress lines from the paste buffer by replacing groups of 8 
03490        // spaces with tabs.
03491 
03492        buffer = StrTool::pack_tabs(insertedLine->c_str());
03493 
03494        insertedLine = &buffer;
03495 
03496     }
03497     else
03498     {
03499        // if we are not saving with tabs, expand any tabs that might be in the line
03500 
03501        buffer = StrTool::expand_tabs(insertedLine->c_str());
03502 
03503        insertedLine = &buffer;
03504 
03505     }
03506 
03507 
03508     lines_.insert( lines_.find(cur_), new std::string(*insertedLine) );
03509 
03510     ++cur_;
03511     ++op->count_;
03512   }
03513 
03514   make_line_visible(vp, v);
03515   paint_page(vp,v);
03516 
03517   return false;
03518 }
03520 
03521 
03522 
03524 AddMapping file_insert_func(CursorWindow::func_insfile, &Impl::file_insert, Modifying);
03525 
03526 bool
03527 Impl::
03528 file_insert(viewport* vp, Viewer*v, input_event const *e)
03529   // read a file into the current buffer above the current line
03530 {
03531 
03532   FileName f(fullname_);
03533 
03534   CursorWindow::FileSelector d("Insert File",  f.dirname() + "*", "");
03535 
03536   std::string rv = d.popup(manager_->window(), v);
03537 
03538   if(rv.empty())
03539      return false;
03540 
03541 
03542   std::ifstream s(rv.c_str());
03543 
03544   if(!s.good())
03545   {
03546       CursorWindow::Message  d("Error");
03547 
03548       d += std::string("Can not open file ") + rv;
03549 
03550       d.popup(manager_->window());
03551 
03552       return false;
03553   }
03554 
03555   UndoOperation* op = push_undo_stack();
03556 
03557   op->code_ = UndoOperation::delete_block;
03558   op->count_= 0;
03559   op->lineno_ = cur_;
03560       
03561 
03562   std::string line;
03563 
03564   while(std::getline(s, line))
03565   {
03566     if(!use_tabs_)
03567        line = StrTool::expand_tabs(line);
03568 
03569     lines_.insert( lines_.find(cur_), new std::string(line) );
03570 
03571     ++cur_;
03572     ++op->count_;
03573   }
03574 
03575   make_line_visible(vp, v);
03576   paint_page(vp,v);
03577 
03578   return false;
03579 }
03581 
03583 AddMapping copy_block_func(CursorWindow::func_cb, &Impl::block_copy, NonModifying);
03584 
03585 bool
03586 Impl::
03587 block_copy(viewport* vp, Viewer*v, input_event const *e)
03588   // copy the marked block to the paste buffer
03589 {
03590   if(first_mark_ == last_mark_)
03591     return false;  // no marked block
03592 
03593   Viewer::paste_buffer_type &paste_buffer = *v->paste_buffer();
03594 
03595   paste_buffer.erase(paste_buffer.begin(), paste_buffer.end());
03596 
03597   // copy the lines and copy them to the paste
03598   // buffer
03599 
03600   while(last_mark_ != first_mark_)
03601   {
03602     // copy marked lines starting at tail to simplify work
03603 
03604     --last_mark_;
03605 
03606     rep_type::iterator scan = lines_.find(last_mark_);
03607 
03608     if(scan != lines_.end())
03609     {
03610       EditLine &el =*scan;
03611 
03612       paste_buffer.insert(paste_buffer.begin(), *el.data());
03613 
03614     }
03615 
03616   }
03617 
03618   v->commitPasteBuffer();  // write the paste buffer to the os
03619 
03620   first_mark_ = last_mark_ = -1;
03621 
03622   paint_page(vp, v);
03623 
03624 
03625   return false;
03626 }
03628 
03630 AddMapping save_block_func(CursorWindow::func_cb, &Impl::block_copy, NonModifying);
03631 
03632 bool
03633 Impl::
03634 block_save(viewport* vp, Viewer*v, input_event const *e)
03635   // save the block to a file
03636 {
03637   if(first_mark_ == last_mark_)
03638   {
03639     vp->beep();
03640 
03641     return false;  // no marked block
03642   }
03643 
03644   FileName f(fullname_);
03645 
03646   CursorWindow::FileSelector d("Save Block to File",  f.dirname() + "*", "");
03647 
03648   std::string target = d.popup(manager_->window(), v);
03649 
03650   if(target.empty())
03651      return false;
03652 
03653   std::ofstream  targetFile(target.c_str());
03654 
03655   if(!targetFile.good())
03656   {
03657      manager_->window()->beep();
03658      return false;
03659   }
03660 
03661   for(int i = first_mark_; i < last_mark_; ++i)
03662   {
03663     rep_type::iterator scan = lines_.find(i);
03664 
03665     if(scan != lines_.end())
03666     {
03667       EditLine &el =*scan;
03668 
03669       std::string data = StrTool::expand_tabs(*el.data());
03670 
03671       targetFile << data << '\n';
03672 
03673     }
03674 
03675   }
03676 
03677 
03678   paint_page(vp, v);
03679 
03680 
03681   return false;
03682 }
03684 
03685 
03687 AddMapping transform_block_func(10000, &Impl::block_transform, Modifying);
03688 
03689 bool
03690 Impl::
03691 block_transform(viewport* vp, Viewer*v, input_event const *e)
03692   // cut the marked block to the paste buffer
03693 {
03694   
03695   
03696   if(first_mark_ == last_mark_)
03697   {
03698     vp->beep();
03699     return false;  // no marked block
03700   }
03701 
03702 
03703   static std::list<std::string>  script;  // maintained across multiple invocations
03704 
03705   if(script.size() == 0)
03706   {
03707       script.push_back("");
03708       script.push_back("p");
03709       script.push_back("");   // always guarantee an empty line at the end
03710   }
03711   else
03712   {
03713      // make sure that there's always a blank line to add one more command if needed.
03714 
03715 
03716      if(!script.back().empty())
03717      {
03718         script.push_back("");
03719      }
03720   }
03721 
03722   bool promptUser=true;
03723   
03724   {
03725       typedef std::pair<Viewer*, FileName> ViewerInfo;
03726 
03727 
03728       std::list<ViewerInfo> viewers;
03729 
03730       manager_->findMatchingViewers(FileName("*.mused"), viewers);
03731 
03732       if(!viewers.empty())
03733       {
03734 
03735          script.clear();
03736 
03737 
03738          Viewer* activeViewer = viewers.front().first;
03739 
03740          if(viewers.size() > 1)
03741          {
03742 
03743              CursorWindow::Selection d("Choose a mused script:");
03744          
03745              CXXTLS_FOREACH(ViewerInfo &cur, viewers)
03746              {
03747                 d += cur.second;
03748              }
03749 
03750 
03751              if(d.popup(manager_->window()))
03752                return false;
03753          
03754              CXXTLS_FOREACH(ViewerInfo &cur, viewers)
03755              {
03756                  if(cur.second == d.selection_)
03757                  {
03758                     activeViewer = cur.first;
03759                     break;
03760                  }
03761              }
03762              
03763          }
03764 
03765 
03766          if(!activeViewer->fetchAllLines(script))
03767          {
03768             
03769              CursorWindow::Message d("Error:  cannot access script");
03770              
03771              d += "Press Enter to continue";
03772              
03773              d.popup(manager_->window());
03774              
03775              return false;
03776          }
03777          else
03778          {
03779             promptUser = false;
03780          }
03781       }
03782 
03783   }
03784 
03785 
03786   
03787   if(promptUser)
03788   {
03789       // create a multi-line string dialog that lets the user
03790       // enter a script.
03791     
03792       CursorWindow::Dialog d("Enter a short MUSED script");
03793     
03794       int count=0;
03795     
03796       CXXTLS_FOREACH(std::string const &cur, script)
03797       {
03798           char name  [40];
03799           char label [40];
03800     
03801           ++count;
03802     
03803           snprintf(name,  sizeof(name),  "cmd%d", count);
03804           snprintf(label, sizeof(label), "%d", count);
03805     
03806     
03807           d += new CursorWindow::Dialog::String(name,
03808                                                 label,
03809                                                 cur,
03810                                                 50
03811                                                 );
03812       }
03813       
03814       // let the user edit the dialog text
03815     
03816       if(d.popup( manager_->window() ))
03817       {
03818         return false;
03819       }
03820     
03821       // fetch the text out of the dialog
03822     
03823       count=0;
03824       CXXTLS_FOREACH(std::string &cur, script)
03825       {
03826           char name  [40];
03827           ++count;
03828     
03829           snprintf(name,  sizeof(name),  "cmd%d", count);
03830     
03831           cur = d.element_value(name);
03832     
03833       }
03834   }
03835 
03836   int saved_first_mark = first_mark_;
03837   int saved_last_mark  = last_mark_;
03838 
03839   UndoOperation::undo_stack* nested_changes = new UndoOperation::undo_stack;
03840   
03841   undo_stack_.swap(*nested_changes);  // temporarily swap the real undo
03842                                       // stack withe the new one just
03843                                       // created so we can then place the
03844                                       // changes caused by replace_next_helper
03845                                       // to be accumulated into the new
03846                                       // stack -- which will then be placed on
03847                                       // the real undo stack
03848 
03849   std::list<std::string>  inputBlock;
03850 
03851 
03852 
03853   // Create an undo operation to hold the deleted lines
03854   {
03855       UndoOperation *op = push_undo_stack();
03856     
03857       op->code_   = UndoOperation::insert_block;
03858       op->lineno_ = first_mark_;
03859     
03860       // delete the lines and copy them to the paste
03861       // buffer
03862     
03863       while(last_mark_ != first_mark_)
03864       {
03865         // delete marked lines starting at tail to simplify work
03866     
03867         --last_mark_;
03868     
03869         rep_type::iterator scan = line_address(last_mark_);
03870     
03871         if(scan != lines_.end())
03872         {
03873           EditLine &el =*scan;
03874 
03875           std::string tmp = el;
03876 
03877           inputBlock.push_front(tmp);   // records are processed in reverse order
03878                                         // because of the way undo works, 
03879                                         // but we need them in forward order
03880                                         // for the muSED::apply operation.
03881     
03882           op->block_.push_back(tmp);
03883     
03884           erase_line(scan, last_mark_, false);  // erase but don't save undo info
03885                                                 // since we are doing it already
03886         }
03887     
03888       }
03889   }
03890 
03891 
03892   std::list<std::string> output;
03893 
03894   std::string error = muSED::apply(script, inputBlock, output);
03895 
03896   {
03897     UndoOperation* op = push_undo_stack();
03898     
03899     op->code_ = UndoOperation::delete_block;
03900     op->count_= 0;
03901     op->lineno_ = first_mark_;
03902 
03903     cur_ = first_mark_;
03904     
03905     std::list<std::string>::iterator first = output.begin(),
03906                                      last  = output.end();
03907 
03908     while(first != last)
03909     {
03910 
03911 
03912       std::string insertedLine = *first++;
03913     
03914       std::string buffer;
03915     
03916       if(use_tabs_)
03917       {
03918          // if we are going to save this file with tabs, then we should 
03919          // tab compress lines from the paste buffer by replacing groups of 8 
03920          // spaces with tabs.
03921     
03922          buffer = StrTool::pack_tabs(insertedLine.c_str());
03923     
03924          insertedLine = buffer;
03925     
03926       }
03927       else
03928       {
03929          // if we are not saving with tabs, expand any tabs that might be in the line
03930     
03931          buffer = StrTool::expand_tabs(insertedLine.c_str());
03932     
03933          insertedLine = buffer;
03934     
03935       }
03936     
03937       lines_.insert( lines_.find(cur_), new std::string(insertedLine) );
03938     
03939       ++cur_;
03940       ++op->count_;
03941     
03942     }
03943   }
03944 
03945   last_mark_ = cur_;
03946 
03947   cur_ = first_mark_;
03948 
03949   {
03950     undo_stack_.swap(*nested_changes);  // put the old undo stack back
03951 
03952 
03953     UndoOperation* op = push_undo_stack();
03954 
03955     op->code_ = UndoOperation::nested_undo_ops;
03956 
03957     op->nested_undo_stack_ = nested_changes;
03958 
03959     op->nested_first_mark_ = saved_first_mark;
03960     op->nested_last_mark_  = saved_last_mark;
03961   }
03962 
03963 
03964   if(!error.empty())
03965   {
03966       CursorWindow::Message d("Script Error");
03967 
03968       d += error;
03969 
03970       d.popup(manager_->window());
03971 
03972   }
03973 
03974   paint_page(vp, v);
03975 
03976 
03977   return false;
03978 }
03980 
03981 
03983 
03984 void
03985 Impl::
03986 save(viewport* vp, TextEditor*v, input_event const *e)
03987 {
03988   if(!is_writeable_)
03989   {
03990     std::string write_anyway("Change to write mode?");
03991 
03992     CursorWindow::Selection d("File not opened for write");
03993 
03994     d += "Abandon attempt to save?";
03995     d += write_anyway;
03996 
03997     if(d.popup(manager_->window()))
03998       return;
03999 
04000     if(d.selection_ == write_anyway)
04001     {
04002       is_writeable_ = true;
04003     }
04004     else
04005     {
04006       return;
04007     }
04008   }
04009 
04010 
04011   FILE *f = fopen(fullname_.c_str(), "wb");
04012 
04013   row_col window_size = vp->window()->size();
04014 
04015   is_dirty_ = false;
04016 
04017   if(!f)
04018   {
04019     vp->beep();
04020 
04021     CursorWindow::Message m( std::string("Error opening ") +
04022                              fullname_.shorten(window_size.col_ - 27) +
04023                              " for write"
04024                            );
04025                         
04026     char buffer[40];
04027 
04028     sprintf(buffer, "errno is %s", strerror(errno));
04029 
04030     m += buffer;
04031     m += "Press enter to continue";
04032 
04033     m.popup(vp->window());
04034 
04035     is_writeable_ = false;
04036 
04037     return;
04038 
04039   }
04040 
04041   bool force_unix_eol=false;
04042 
04043   #ifdef _MSC_VER
04044     if( 
04045         getenv("HOME") && 
04046         (getenv("SFUDIR") || getenv("CYGWIN") )
04047       )
04048       force_unix_eol = true;
04049   #endif
04050 
04051   rep_type::iterator first = lines_.begin(),
04052                      last  = lines_.end();
04053 
04054   while(first != last)
04055   {
04056     std::string data;
04057 
04058     bool addCR = (*first).crRemoved_;
04059 
04060     
04061     if(use_tabs_)
04062       data = (*first++).maybe_tabbify();
04063     else
04064       data = *(*first++).data();
04065 
04066     fwrite(data.data(), data.size(), 1, f);
04067     
04068     if(force_unix_eol)
04069     {
04070       char eol='\n';
04071       fwrite(&eol, 1, 1, f);
04072     }
04073     else
04074     {
04075       if(addCR)
04076       {
04077          // on unix and linux, this flag might be set but on
04078          // dos it won't ever be.
04079 
04080          fwrite("\r\n", 2, 1, f); // add full dos eol sequence
04081 
04082        
04083       }
04084       else
04085            fwrite(&EOL_Sequence, sizeof(EOL_Sequence), 1, f);
04086     }
04087 
04088   }
04089 
04090 
04091   fclose(f);
04092 }
04094 
04096 AddMapping line_goto_func(CursorWindow::func_goto, &Impl::line_goto, NonModifying);
04097 
04098 bool
04099 Impl::
04100 line_goto(viewport* vp, Viewer*v, input_event const *e)
04101   // prompt the user and goto a specified line
04102 {
04103 
04104   StreamableString ss;
04105 
04106   ss << "Goto a line between 1 and " << lines_.size();
04107 
04108 
04109   CursorWindow::Dialog dialog(ss);
04110 
04111   StreamableString lineno;
04112 
04113   lineno << (1+cur_);
04114 
04115   dialog += new CursorWindow::Dialog::String("line",
04116                                              "Goto line",
04117                                              lineno,
04118                                              12
04119                                             );
04120                                         
04121 
04122   dialog += new CursorWindow::Dialog::Ok("ok","");
04123 
04124   if( dialog.popup(vp->window()) )
04125     return false;
04126 
04127   int target_line=1;
04128 
04129   sscanf(dialog.element_value("line").c_str(), "%d", &target_line);
04130 
04131   --target_line;
04132 
04133 
04134   if(target_line < 0 || target_line >= lines_.size())
04135     return false;
04136 
04137   cur_ = target_line;
04138 
04139   make_line_visible(vp,v);
04140 
04141   restore_cursor(vp);
04142 
04143   return false;
04144 }
04145 
04147 
04149 AddMapping line_undo(CursorWindow::func_undo, &Impl::undo, Modifying);
04150 
04151 bool
04152 Impl::
04153 undo(viewport* vp, Viewer*v, input_event const *e)
04154   // pop the undo stack and perform the reverse operation of that
04155   // action
04156 {
04157   bool painting=true;  // update screen after changes
04158 
04159   if(!e)
04160     painting = false; // nested undo's don't paint, only when finished
04161 
04162 
04163   if(undo_stack_.begin() != undo_stack_.end())
04164   {
04165     // there is something to undo
04166 
04167     std::auto_ptr<UndoOperation> op(undo_stack_.front());
04168 
04169     undo_stack_.pop_front();
04170 
04171     is_dirty_ = true;
04172 
04173     switch(op->code_)
04174     {
04175       case  UndoOperation::replace_line:
04176           replace_line(op->lineno_, op->text_, false);
04177           cur_ = op->lineno_;
04178           if(painting)
04179           {
04180             make_line_visible(vp,v);
04181             paint_page(vp,v);
04182           }
04183         break;
04184 
04185       case  UndoOperation::insert_line:
04186           lines_.insert( lines_.find(op->lineno_), new std::string(op->text_));
04187           cur_ = op->lineno_;
04188           if(painting)
04189           {
04190             make_line_visible(vp,v);
04191             paint_page(vp,v);
04192           }
04193         break;
04194       case  UndoOperation::erase_line:
04195           lines_.erase( lines_.find(op->lineno_) );
04196           cur_ = op->lineno_;
04197           if(painting)
04198           {
04199             make_line_visible(vp,v);
04200             paint_page(vp,v);
04201           }
04202         break;
04203         
04204       case  UndoOperation::break_lines:
04205           lines_.erase( lines_.find(op->lineno_) );
04206           cur_ = op->lineno_;
04207           {
04208             std::list<std::string>::iterator first = op->block_.begin(),
04209                                              last  = op->block_.end();
04210                                         
04211             while(last != first)
04212             {
04213               --last;
04214               std::string &r = *last;
04215         
04216               lines_.insert(lines_.find(cur_), new std::string(r));
04217             }
04218           }
04219         
04220           if(painting)
04221           {
04222             make_line_visible(vp,v);
04223             paint_page(vp,v);
04224           }
04225         break;
04226         
04227 
04228       case  UndoOperation::join_lines:
04229 
04230           if(op->lineno_ < lines_.size()-1)
04231           {
04232             std::string current_line_text = line_text(op->lineno_);
04233         
04234             col_ = current_line_text.size();
04235         
04236             cur_ = op->lineno_;
04237         
04238             lines_.erase( lines_.find(cur_+1) );
04239         
04240             replace_line(cur_, op->text_, false);
04241         
04242             if(painting)
04243             {
04244               make_line_visible(vp,v);
04245               paint_page(vp,v);
04246             }
04247           }
04248         break;
04249         
04250       case  UndoOperation::insert_block:
04251         {
04252           std::list<std::string>::iterator first = op->block_.begin(),
04253                                            last  = op->block_.end();
04254                                         
04255           while(first != last)
04256           {
04257             lines_.insert(lines_.find(op->lineno_), new std::string(*first));
04258             ++first;
04259           }
04260 
04261           if(painting)
04262             paint_page(vp,v);
04263         
04264         }
04265         break;
04266         
04267       case  UndoOperation::replace_block:
04268         {
04269           std::list<std::string>::iterator first = op->block_.begin(),
04270                                            last  = op->block_.end();
04271                                         
04272           while(first != last)
04273           {
04274             replace_line(op->lineno_++, std::string(*first), false);
04275         
04276             ++first;
04277           }
04278 
04279           if(painting)
04280             paint_page(vp,v);
04281         
04282         }
04283         break;
04284         
04285         
04286       case  UndoOperation::delete_block:
04287         {
04288           // this algorithm is necessary because lines_ is a
04289           // vector and whenever you insert or delete into a vector
04290           // _all_ iterators are invalid
04291         
04292         
04293           while(op->count_)
04294           {
04295             rep_type::iterator first = lines_.find(op->lineno_);
04296         
04297             lines_.erase(first);
04298         
04299              --op->count_;
04300           }
04301                 
04302           cur_ = op->lineno_;
04303         
04304           if(painting)
04305           {
04306             make_line_visible(vp,v);
04307             paint_page(vp,v);
04308           }
04309         
04310         }
04311         break;
04312         
04313       case UndoOperation::nested_undo_ops:
04314         {
04315            UndoOperation::undo_stack* nested_changes = op->nested_undo_stack_;
04316         
04317            undo_stack_.swap(*nested_changes);
04318         
04319            bool something_to_do = !undo_stack_.empty();
04320         
04321            while(!undo_stack_.empty())
04322            {
04323              undo(vp,v,0);  // the 0 for e is flag saying don't paint
04324            }
04325         
04326            if(something_to_do)
04327              is_dirty_ = true;
04328         
04329            undo_stack_.swap(*nested_changes);
04330         
04331            delete nested_changes;
04332 
04333            first_mark_ = op->nested_first_mark_;
04334            last_mark_  = op->nested_last_mark_;
04335         
04336            if(painting)
04337            {
04338              make_line_visible(vp,v);
04339              paint_page(vp,v);
04340            }
04341         
04342         }
04343         break;
04344     }
04345 
04346   }
04347   else
04348   {
04349     vp->beep();
04350     is_dirty_ = false;
04351   }
04352 
04353   return false;
04354 }
04355 
04357 bool
04358 Impl::
04359 show_symbols(viewport* vp, Viewer*v, input_event const *e)
04360 {
04361 
04362 
04363   std::string name = fullname_ + ".cpptagdb";
04364 
04365   Viewer* oldie = manager_->find_viewer(name);
04366 
04367   if(oldie)
04368   {
04369     manager_->activate(oldie);
04370   }
04371   else
04372   {
04373 
04374     manager_->window()->close();
04375 
04376     Viewer *newbie = manager_->open(name);  // name is the "current" file name plus .cpptagdb.  The
04377                                             // symbol viewer knows to use cpptagdb.exe to get the
04378                                             // correct output instead of actually reading files whose
04379                                             // name ends in .cpptagdb
04380 
04381     manager_->window()->open();
04382 
04383     if(!newbie)
04384       manager_->window()->beep();
04385     else
04386     {
04387       manager_->hsplit(newbie, name);
04388     }
04389 
04390   }
04391 
04392 
04393   return false;
04394 }
04395 
04396 
04397 
04399 
04400 
04401 struct Is_Path_Separator
04402 {
04403   bool operator() (char c) const
04404   {
04405     return c == FileName::PATH_separator ;
04406   }
04407 
04408 };
04409 
04410 bool
04411 Impl::
04412 snatch_edit(viewport* vp, Viewer*v, input_event const *e)
04413   //
04414   // Snatch the text under the cursor and assume it is a filename and bring up
04415   // an edit session on that file.  Also, if the text under the cursor looks
04416   // like a recognized compiler error message or grep output, make sure the
04417   // current line is positioned correctly in the edited file.
04418   //
04419 {
04420 
04421   std::string s = line_text(cur_);
04422 
04423   size_t rLength = s.size();
04424 
04425   if( col_ >= rLength || s[col_] == ' ')
04426   { // not sitting on a word
04427 
04428     manager_->window()->beep();
04429 
04430     return false;
04431 
04432   } // not sitting on a word
04433 
04434   std::string tmp( s.begin() + col_, s.end());
04435 
04436   FileName name;
04437   int      number=0;
04438 
04439   if(!StrTool::snatch_file_info(tmp, name, number))
04440   {
04441     manager_->window()->beep();
04442 
04443     return false;
04444 
04445   }
04446 
04447   name = manager_->find_file_in_standard_locations(name, fullname_.dirname());
04448 
04449   if(name.empty())
04450   {
04451     manager_->window()->beep();
04452 
04453     return false;
04454 
04455   }
04456 
04457   Viewer* oldie = manager_->find_viewer(name);
04458 
04459   if(oldie)
04460   {
04461     oldie->set_row_col_hint(number-1,0);
04462     manager_->activate(oldie);
04463   }
04464   else
04465   {
04466     if(!name.exists())
04467     {
04468        CursorWindow::Message m(name + " does not exist");
04469 
04470        m += "Sorry, can't find it";
04471        m += "";
04472        m += "Press Enter to continue";
04473 
04474        m.popup(manager_->window());
04475 
04476        return false;
04477     }
04478 
04479     Viewer *newbie = manager_->edit(name);
04480 
04481     if(!newbie)
04482       manager_->window()->beep();
04483     else
04484     {
04485       newbie->set_row_col_hint(number-1, 0);  // 0 based line numbers!
04486       manager_->vsplit(newbie, name);
04487     }
04488 
04489   }
04490 
04491 
04492   return false;
04493 }
04494 
04495 void
04496 TextEditor::
04497 set_row_col_hint(size_t line_setting, size_t col, bool force_repaint)
04498 {
04499 
04500   int line = line_setting;
04501 
04502   if(line >= 0 && line < impl_->size())
04503   {
04504     impl_->cur_ = line;
04505     impl_->page_top_ = line;
04506     impl_->col_ = col;
04507     impl_->page_left_ = col;
04508   }
04509 
04510 }
04511 
04512 void
04513 TextEditor::
04514 set_read_only()
04515 {
04516   impl_->is_writeable_ = false;
04517 }
04518 
04519 
04520 
04521 
04522 bool
04523 Impl::
04524 word_sfind(viewport* vp, Viewer*v, input_event const *e)
04527 {
04528   std::string curline = line_text(cur_);
04529 
04530   if(col_ >= curline.size() || curline[col_] == ' ')
04531   {
04532     vp->beep();
04533     return false;
04534   }
04535 
04536   std::string::const_iterator first = curline.begin() + col_,
04537                               last  = curline.end();
04538                         
04539   search_text_ = "";
04540 
04541   while(first != last && is_word_char(*first) )
04542   {
04543     search_text_ += *first++;
04544   }
04545 
04546   if(search_text_.size() == 0)
04547   {
04548     vp->beep();
04549     return false;
04550   }
04551 
04552   size_t col_save = col_;
04553   int    cur_save = cur_;
04554 
04555   if(find_next_helper(vp, v, e))
04556   {
04557     make_line_visible(vp,v);
04558     make_col_visible(vp,v);
04559   }
04560   else
04561   {
04562     vp->beep();
04563     col_ = col_save;
04564     cur_ = cur_save;
04565   }
04566 
04567   return false;
04568 }
04569 
04570 bool
04571 Impl::
04572 etags_first(viewport* vp, Viewer*v, input_event const *e)
04575 {
04576   EtagsDB* etags=(manager_->default_etags_db());  // pts to static object
04577 
04578 
04579   if(!etags || !etags->ok())
04580   {
04581     // error message will have been handled by ViewerManager code
04582     return false;
04583   }
04584 
04585   std::string symbol;
04586   std::string file;
04587   int         line;
04588   std::string matching_text;
04589 
04590   std::string curline = line_text(cur_);
04591 
04592   if( !(col_ >= curline.size() || curline[col_] == ' ') )
04593   {
04594     std::string::const_iterator first = curline.begin() + col_,
04595                                 last  = curline.end();
04596                         
04597     while(first != last && is_word_char(*first) )
04598     {
04599       symbol += *first++;
04600     }
04601   }
04602 
04603 
04604   CursorWindow::Dialog d("Search TAGS (etags file) For A Symbol");
04605 
04606   d += new CursorWindow::Dialog::String("symbol",
04607                                         "Symbol Name",
04608                                         symbol,
04609                                         40
04610                                         );
04611                                 
04612   if(d.popup(manager_->window()))
04613     return 0;
04614 
04615   symbol = d.element_value("symbol");
04616 
04617   if(symbol.size() == 0)
04618   {
04619     vp->beep();
04620     return false;
04621   }
04622 
04623   bool rv = etags->find_first(symbol, &file, &line, &matching_text);
04624 
04625   if(rv)
04626   {
04627     Viewer* oldie = manager_->find_viewer(file);
04628 
04629     if(oldie)
04630     {
04631       oldie->set_row_col_hint(line-1,0);
04632       manager_->activate(oldie);
04633     }
04634     else
04635     {
04636 
04637       Viewer* newbie = manager_->edit(file);
04638 
04639       if(newbie)
04640       {
04641         newbie->set_row_col_hint(line-1,0);
04642 
04643         manager_->vsplit(newbie, file);
04644 
04645       }
04646       else
04647         vp->beep();
04648     }
04649 
04650   }
04651 
04652   return false;
04653 }
04654 
04655 bool
04656 Impl::
04657 cpptags_first(viewport* vp, Viewer*v, input_event const *e)
04660 {
04661   CppTagDB::Info* cpptags=(manager_->default_cpptags_db());  // pts to static object
04662 
04663 
04664   if(!cpptags)
04665   {
04666     // error message will have been handled by ViewerManager code
04667     return false;
04668   }
04669 
04670   std::string symbol;
04671   std::string file;
04672   int         line;
04673   std::string matching_text;
04674 
04675   std::string curline = line_text(cur_);
04676 
04677   if( !(col_ >= curline.size() || curline[col_] == ' ') )
04678   {
04679     std::string::const_iterator first = curline.begin() + col_,
04680                                 last  = curline.end();
04681                         
04682     while(first != last && is_word_char(*first) )
04683     {
04684       symbol += *first++;
04685     }
04686   }
04687 
04688 
04689   CursorWindow::Dialog d("Search TAGPP.tagpp (cpptagdb.exe file) For A Symbol");
04690 
04691   d += new CursorWindow::Dialog::String("symbol",
04692                                         "Symbol Name",
04693                                         symbol,
04694                                         40
04695                                         );
04696                                 
04697   if(d.popup(manager_->window()))
04698     return 0;
04699 
04700   symbol = d.element_value("symbol");
04701 
04702   if(symbol.size() == 0)
04703   {
04704     vp->beep();
04705     return false;
04706   }
04707 
04708   std::vector<cxxtls::CppTagDB::SymbolInfo const *> matches;
04709 
04710   cpptags->findMatchingMembers(symbol, matches);
04711 
04712   if(!matches.empty())
04713   {
04714     std::vector<ViewerManager::FileLocation>  locations;
04715 
04716     CXXTLS_FOREACH(CppTagDB::SymbolInfo const *cur, matches)
04717     {
04718        locations.push_back( ViewerManager::FileLocation(cur->file_, cur->line_) );
04719     }
04720 
04721     manager_->setFileLocations(locations);
04722 
04723     ViewerManager::FileLocation tmp;
04724 
04725     manager_->popFileLocation(tmp);
04726 
04727     file = tmp.filename_;
04728     line = tmp.lineNumber_;
04729 
04730 
04731     Viewer* oldie = manager_->find_viewer(file);
04732 
04733     if(oldie)
04734     {
04735       oldie->set_row_col_hint(line-1,0);
04736       manager_->activate(oldie);
04737     }
04738     else
04739     {
04740 
04741       Viewer* newbie = manager_->edit(file);
04742 
04743       if(newbie)
04744       {
04745         newbie->set_row_col_hint(line-1,0);
04746 
04747         manager_->vsplit(newbie, file);
04748 
04749       }
04750       else
04751         vp->beep();
04752     }
04753 
04754   }
04755   else
04756      vp->beep();
04757 
04758   return false;
04759 }
04760 
04761 
04762 bool
04763 Impl::
04764 etags_all(viewport* vp, Viewer*v, input_event const *e)
04767 {
04768   EtagsDB* etags=(manager_->default_etags_db()); // pts to static object
04769 
04770   if(!etags || !etags->ok())
04771   {
04772     // error message will have been handled by ViewerManager code
04773     return false;
04774   }
04775 
04776   std::string symbol;
04777   std::string file;
04778   int         line;
04779   std::string matching_text;
04780 
04781   std::string curline = line_text(cur_);
04782 
04783   if( !(col_ >= curline.size() || curline[col_] == ' ') )
04784   {
04785     std::string::const_iterator first = curline.begin() + col_,
04786                                 last  = curline.end();
04787                         
04788     while(first != last && is_word_char(*first) )
04789     {
04790       symbol += *first++;
04791     }
04792   }
04793 
04794 
04795   CursorWindow::Dialog d("Search TAGS (etags file) For All Matching Symbols");
04796 
04797   d += new CursorWindow::Dialog::String("symbol",
04798                                         "Symbol Name",
04799                                         symbol,
04800                                         40
04801                                         );
04802                                 
04803   if(d.popup(manager_->window()))
04804     return 0;
04805 
04806   symbol = d.element_value("symbol");
04807 
04808   if(symbol.size() == 0)
04809   {
04810     vp->beep();
04811     return false;
04812   }
04813 
04814   bool rv = etags->find_first(symbol, &file, &line, &matching_text);
04815 
04816   if(rv)
04817   {
04818       CursorWindow::Selection d("Symbols matching " + symbol);
04819     
04820       d += "";
04821 
04822       while(rv)
04823       {
04824          std::string tmp = file;
04825 
04826          char buffer[40];
04827 
04828          snprintf(buffer, 40, ":%d", line);
04829 
04830          tmp += buffer;
04831          tmp += ' ';
04832          tmp += matching_text;
04833 
04834          d += tmp;
04835 
04836          rv = etags->find_next(&file, &line, &matching_text);
04837 
04838       }
04839 
04840       d += "";
04841 
04842       d += "Press Enter to continue";
04843 
04844       if(!d.popup(manager_->window()))
04845       {
04846           // user didn't abort
04847 
04848           std::string const &selection = d.selection_;
04849 
04850           size_t colon = selection.find_first_of(':');
04851 
04852           if(colon >= selection.size())
04853           {
04854             vp->beep();  // should never get here
04855           }
04856           else
04857           {
04858             // there is a filename:lineNumber pair
04859 
04860             file = selection.substr(0, colon);
04861 
04862             std::string tmp = selection.substr(colon+1, selection.size() - colon -1);
04863 
04864             line = 0;
04865 
04866             sscanf(tmp.c_str(), "%d", &line);
04867 
04868             Viewer* oldie = manager_->find_viewer(file);
04869         
04870             if(oldie)
04871             {
04872               oldie->set_row_col_hint(line-1,0);
04873               manager_->activate(oldie);
04874             }
04875             else
04876             {
04877         
04878               Viewer* newbie = manager_->edit(file);
04879         
04880               if(newbie)
04881               {
04882                 newbie->set_row_col_hint(line-1,0);
04883         
04884                 manager_->vsplit(newbie, file);
04885         
04886               }
04887               else
04888                 vp->beep();
04889             }
04890 
04891           }
04892           
04893 
04894       }
04895 
04896   }
04897   else
04898     vp->beep();
04899 
04900   return false;
04901 }
04902 
04903 bool
04904 Impl::
04905 cpptags_all(viewport* vp, Viewer*v, input_event const *e)
04908 {
04909 
04910   CppTagDB::Info* cpptags=(manager_->default_cpptags_db());  // pts to static object
04911 
04912   if(!cpptags)
04913   {
04914     // error message will have been handled by ViewerManager code
04915     return false;
04916   }
04917 
04918   std::string symbol;
04919   std::string file;
04920   int         line;
04921   std::string matching_text;
04922 
04923 
04924   std::string curline = line_text(cur_);
04925 
04926   if( !(col_ >= curline.size() || curline[col_] == ' ') )
04927   {
04928     std::string::const_iterator first = curline.begin() + col_,
04929                                 last  = curline.end();
04930                         
04931     while(first != last && is_word_char(*first) )
04932     {
04933       symbol += *first++;
04934     }
04935   }
04936 
04937 
04938   CursorWindow::Dialog d("Search TAGPP.tagpp (cpptagdb.exe file) For All Matching Symbols");
04939 
04940   d += new CursorWindow::Dialog::String("symbol",
04941                                         "Symbol Name",
04942                                         symbol,
04943                                         40
04944                                         );
04945                                 
04946   if(d.popup(manager_->window()))
04947     return 0;
04948 
04949   symbol = d.element_value("symbol");
04950 
04951   if(symbol.size() == 0)
04952   {
04953     vp->beep();
04954     return false;
04955   }
04956 
04957   std::vector<cxxtls::CppTagDB::SymbolInfo const *> matches;
04958 
04959  cpptags->findMatchingMembers(symbol, matches, false);  // insensitive comparison
04960 
04961   if(!matches.empty())
04962   {
04963       CursorWindow::Selection d("Symbols matching " + symbol);
04964     
04965       d += "";
04966 
04967       std::vector<cxxtls::CppTagDB::SymbolInfo const *>::const_iterator first, last;
04968 
04969       for(first = matches.begin(), last = matches.end(); first != last; ++first)
04970       {
04971          std::string tmp = (*first)->file_;
04972 
04973          char buffer[40];
04974 
04975          snprintf(buffer, 40, ":%d", (*first)->line_);
04976 
04977          tmp += buffer;
04978 
04979          d += tmp;
04980 
04981       }
04982 
04983       d += "";
04984 
04985       d += "Press Enter to continue";
04986 
04987       if(!d.popup(manager_->window()))
04988       {
04989           // user didn't abort
04990 
04991           std::string const &selection = d.selection_;
04992 
04993           size_t colon = selection.find_first_of(':');
04994 
04995           if(colon >= selection.size())
04996           {
04997             vp->beep();  // should never get here
04998           }
04999           else
05000           {
05001             // there is a filename:lineNumber pair
05002 
05003             file = selection.substr(0, colon);
05004 
05005             std::string tmp = selection.substr(colon+1, selection.size() - colon -1);
05006 
05007             line = 0;
05008 
05009             sscanf(tmp.c_str(), "%d", &line);
05010 
05011             Viewer* oldie = manager_->find_viewer(file);
05012         
05013             if(oldie)
05014             {
05015               oldie->set_row_col_hint(line-1,0);
05016               manager_->activate(oldie);
05017             }
05018             else
05019             {
05020         
05021               Viewer* newbie = manager_->edit(file);
05022         
05023               if(newbie)
05024               {
05025                 newbie->set_row_col_hint(line-1,0);
05026         
05027                 manager_->vsplit(newbie, file);
05028         
05029               }
05030               else
05031                 vp->beep();
05032             }
05033 
05034           }
05035           
05036 
05037       }
05038 
05039   }
05040   else
05041     vp->beep();
05042 
05043   return false;
05044 }
05045 
05046 
05047 
05048 bool
05049 Impl::
05050 tags_next(viewport* vp, Viewer*v, input_event const *e)
05055 {
05056 
05057   std::string file;
05058   int         line;
05059   std::string matching_text;
05060 
05061   ViewerManager::FileLocation loc;
05062 
05063 
05064   if(manager_->popFileLocation(loc))
05065   {
05066      // there is a saved file location
05067 
05068 
05069      file = loc.filename_;
05070      line = loc.lineNumber_;
05071 
05072   }
05073   else
05074   {
05075       EtagsDB *etags=(manager_->default_etags_db());  // pts to static object
05076 
05077       if(etags && etags->ok())
05078       {
05079         if(etags->find_next(&file, &line, &matching_text))
05080         {
05081            // found file and line
05082         }
05083         else
05084         {
05085            // no file location and unfortunately the etags
05086            // database does not contain the requested data.
05087       
05088       
05089            vp->beep();
05090            return false;
05091         }
05092       }
05093       else
05094       {
05095         // neither etags nor saved file locations could be accessed
05096       
05097         vp->beep();
05098       
05099       
05100         return false;
05101       }
05102   }
05103 
05104   Viewer* oldie = manager_->find_viewer(file);
05105   
05106   if(oldie)
05107   {
05108     oldie->set_row_col_hint(line-1,0);
05109     manager_->activate(oldie);
05110   }
05111   else
05112   {
05113   
05114     Viewer* newbie = manager_->edit(file);
05115   
05116     if(newbie)
05117     {
05118       newbie->set_row_col_hint(line-1,0);
05119   
05120       manager_->add(newbie, file);
05121   
05122     }
05123     else
05124       vp->beep();
05125   }
05126 
05127 
05128   return false;
05129 }
05130 
05132 AddMapping matching_char(CursorWindow::func_matching, &Impl::char_matching, NonModifying);
05133 
05134 
05135 
05136 TextIterator
05137 Impl::
05138 top_pointer()
05139 {
05140   return TextIterator(lines_, 0, 0);
05141 }
05142 
05143 
05144 TextIterator
05145 Impl::
05146 bot_pointer()
05147 {
05148   return TextIterator(lines_, lines_.size(), 0);
05149 }
05150 
05151 TextIterator
05152 Impl::
05153 cur_pointer()
05154 {
05155   std::string curline = line_text(cur_);
05156 
05157   if(cur_ >= lines_.size())
05158     return TextIterator(lines_, lines_.size(), 0);
05159   else
05160   if(col_ >= curline.size())
05161     return TextIterator(lines_, cur_, curline.size());
05162 
05163   return TextIterator(lines_, cur_, col_);
05164 }
05165 
05166 bool
05167 Impl::
05168 char_matching(viewport* vp, Viewer*v, input_event const *e)
05169 {
05170   // find the character matching the one under the cursor
05171 
05172   TextIterator top = top_pointer(),  // get iterator to top of the edit session
05173                cur = cur_pointer(),  // get iterator to current file position
05174                bot = bot_pointer();  // get iterator to the bottom of the file
05175 
05176   if(cur == bot)
05177   {
05178     //
05179     // at end of file...
05180     //
05181     vp->beep();
05182     return false;
05183   }
05184 
05185 
05186   char match = *cur;
05187   bool down;
05188   char orig  = match;
05189 
05190   switch(match)
05191   {
05192     case '{':  match = '}'; down=true; break;
05193     case '(':  match = ')'; down=true; break;
05194     case '<':  match = '>'; down=true; break;
05195     case '[':  match = ']'; down=true; break;
05196 
05197     case '}':  match = '{'; down=false; break;
05198     case ')':  match = '('; down=false; break;
05199     case '>':  match = '<'; down=false; break;
05200     case ']':  match = '['; down=false; break;
05201 
05202     default:
05203 
05204       vp->beep();
05205       return false;
05206   }
05207 
05208   if(down)
05209   {
05210     // parse C++ tokens forward through the file until the match is found
05211 
05212     int depth = 0;
05213 
05214     while(cur != bot)
05215     {
05216       char c = *cur;
05217 
05218       if(c == orig)
05219         ++depth;
05220       else
05221       if(c == match)
05222       {
05223         --depth;
05224         if(depth == 0)
05225           break;
05226       }
05227       else
05228       if(c == '\'')
05229       {
05230         ++cur;
05231         
05232         while(cur != bot && *cur != '\'')
05233         {
05234           // skip quoted strings
05235         
05236           if(*cur == '\\') // skip any escape sequence
05237             ++cur;
05238         
05239           if(cur != bot)  // skip the current char
05240             ++cur;
05241         }
05242         
05243       }
05244       else
05245       if(c == '"')
05246       {
05247         ++cur;
05248         
05249         while(cur != bot && *cur != '"')
05250         {
05251           // skip quoted strings
05252         
05253           if(*cur == '\\') // skip any escape sequence
05254             ++cur;
05255         
05256           if(cur != bot)  // skip the current char
05257             ++cur;
05258         }
05259         
05260       }
05261       else
05262       if(c == '/')
05263       {
05264         // handle c++ style comments
05265 
05266         ++cur;
05267 
05268         if( (cur != bot) && (*cur == '/') )
05269         {
05270           while(  (cur != bot) && (*cur  != '\n') )
05271             ++cur;
05272         }
05273 
05274 
05275       }
05276       
05277       if(cur != bot)
05278         ++cur;
05279     }
05280 
05281   }
05282   else
05283   {
05284     // parse C++ tokens backwards through the file until the match is found
05285 
05286     int depth = 0;
05287 
05288     while(cur != top)
05289     {
05290       char c = *cur;
05291 
05292       if(c == orig)
05293         ++depth;
05294       if(c == match)
05295       {
05296         --depth;
05297         if(depth == 0)
05298           break;
05299       }
05300       else
05301       if(c == '\'' )
05302       { 
05303 
05304         // skip the trailing single quoted string
05305         // scan backwards from the last valid char in the string to the first
05306         // ' in the string -- taking into account this kind of annoyance:  '\''
05307         //                                                                   ^
05308         
05309         --cur;
05310 
05311         while(cur != top)
05312         {
05313            if(*cur != '\'')
05314              --cur;
05315            else
05316            {
05317              // ok, we have a ' -- but is this:  'asdf\'adsf'
05318              //                                        ^
05319 
05320              --cur; 
05321 
05322              if(*cur != '\\' )
05323              {
05324                ++cur;  // cur will get decremented later in a the outer loop so make sure
05325                        // the cursor is not already decrement once by mistake
05326                break;  
05327              }
05328              else
05329                --cur;
05330            }
05331         }
05332 
05333       } 
05334       else
05335       if(c == '"')
05336       { 
05337 
05338         // skip the trailing single quoted string
05339         // scan backwards from the last valid char in the string to the first
05340         // " in the string -- taking into account this kind of annoyance:  "\""
05341         //                                                                   ^
05342         
05343         --cur;
05344 
05345         while(cur != top)
05346         {
05347            char c = *cur;  // for debugging purposes
05348 
05349            (void)(c); // shut the stupid warning the F* off.
05350 
05351            if(*cur != '"')
05352              --cur;
05353            else
05354            {
05355              // ok, we have a ' -- but is this:  "asdf\"adsf"
05356              //                                        ^
05357 
05358              --cur; 
05359 
05360              if(*cur != '\\' )
05361              {
05362                ++cur;  // cur will get decremented later in a the outer loop so make sure
05363                        // the cursor is not already decrement once by mistake
05364                break;  
05365              }
05366              else
05367                --cur;
05368            }
05369         }
05370 
05371       } 
05372 
05373       if( (cur != top) && (*cur == '\n') )
05374       {
05375         // step back to the previous line and remove any c++ style comments on it
05376 
05377         --cur;
05378 
05379         TextIterator here(cur);
05380 
05381         char c = *cur;
05382 
05383         //        while( (cur != top) && (*cur != '\n') && (*cur != '"') && (*cur != '\'') )
05384 
05385         while( (cur != top) && (*cur != '\n') )
05386         {
05387           c = *cur;
05388           --cur;
05389         }
05390 
05391         // cur now sits at the beginning of the previous line -- scan forward until // is found
05392         // or until cur == here -- if // is found, stop and make that be 'here'
05393 
05394         for(;;)
05395         {
05396           if(cur == here)
05397             break;
05398 
05399           if(*cur == '/' && (cur != here) )
05400           {
05401             ++cur;
05402 
05403             if(cur != here && (*cur == '/'))
05404             {
05405                here = cur;
05406                break; 
05407 
05408                // leave here pointing to the comment -- it won't cause any trouble
05409             }
05410 
05411           }
05412           else
05413             ++cur;
05414         }
05415 
05416         cur = here;
05417 
05418         ++cur; // so the following decrement will work...
05419       }
05420 
05421 
05422       if(cur != top)
05423         --cur;
05424     }
05425   }
05426     
05427   if(cur == bot || cur == top)
05428   {
05429     //
05430     // found no match
05431     //
05432     vp->beep();
05433     return false;
05434   }
05435 
05436 
05437   cur_ = cur.line_;
05438   col_ = cur.col_;
05439 
05440   make_line_visible(vp,v);
05441   make_col_visible(vp,v);
05442 
05443   return false;
05444 }
05445 
05447 
05448 bool
05449 Impl::
05450 block_insert(viewport* vp, Viewer*v, input_event const *e)
05451 {
05452   // if a block is marked, prompt the user for a string to
05453   // insert into the block at the current column.  Each marked line
05454   // gets the inserted text.
05455 
05456 
05457   if(first_mark_ == last_mark_)
05458   {
05459     vp->beep();
05460     return false;
05461   }
05462 
05463   CursorWindow::Dialog d("Enter a string to insert into the block");
05464 
05465   d += new CursorWindow::Dialog::String("name",
05466                                         "Text to insert",
05467                                         "",
05468                                         40
05469                                        );
05470                                 
05471   d += new CursorWindow::Dialog::Ok("ok","");
05472 
05473   if(d.popup(vp->window()))
05474     return false;
05475 
05476   std::string text = d.element_value("name");
05477 
05478   if(text.size() == 0)
05479     return false;
05480 
05481   int i;
05482 
05483   UndoOperation* op = push_undo_stack();
05484 
05485   op->code_   =  UndoOperation::replace_block;
05486   op->lineno_ =  first_mark_;
05487 
05488   for(i=first_mark_; i < last_mark_; ++i)
05489   {
05490     std::string curline = line_text(i);
05491 
05492     op->block_.push_back(lines_[i]);
05493 
05494     if(curline.size() < col_)
05495     {
05496        size_t pad = col_ - curline.size();
05497 
05498        curline += std::string(pad, ' ');
05499     }
05500 
05501     curline.insert(col_, text);
05502 
05503     replace_line(i, curline, false);  // don't include undo info, we've done it alreay
05504 
05505   }
05506 
05507   paint_page(vp,v);
05508 
05509   return false;
05510 }
05511 
05513 
05515 
05516 bool
05517 Impl::
05518 block_adjust(viewport* vp, Viewer*v, input_event const *e)
05519 {
05520   // Prompt the user for a number of columns to insert into the
05521   // marked block.  That is, insert a column of spaces at the current
05522   // cursor column into all lines in the marked block.
05523 
05524   if(first_mark_ == last_mark_)
05525   {
05526     vp->beep();
05527     return false;
05528   }
05529 
05530   CursorWindow::Dialog d("Insert/delete colums");
05531 
05532   d += new CursorWindow::Dialog::String("name",
05533                                         "Columns to insert/delete",
05534                                         "0",
05535                                         10
05536                                        );
05537                                 
05538   d += new CursorWindow::Dialog::Ok("ok","");
05539                                 
05540                                 
05541   if(d.popup(vp->window()))
05542     return false;
05543 
05544   std::string text = d.element_value("name");
05545 
05546   if(text.size() == 0)
05547     return false;
05548 
05549   int adjustment =0;
05550 
05551   sscanf(text.c_str(), "%d", &adjustment);
05552 
05553   if(adjustment == 0)
05554     return false;
05555 
05556 
05557   int i;
05558 
05559   UndoOperation* op = push_undo_stack();
05560 
05561   op->code_   =  UndoOperation::replace_block;
05562   op->lineno_ =  first_mark_;
05563 
05564   std::string spaces_to_insert( adjustment > 0 ? adjustment : 0, ' ');
05565 
05566   for(i=first_mark_; i < last_mark_; ++i)
05567   {
05568     std::string curline = line_text(i);
05569 
05570     op->block_.push_back(lines_[i]);
05571 
05572     if(curline.size() < col_)
05573     {
05574        size_t pad = col_ - curline.size();
05575 
05576        curline += std::string(pad, ' ');
05577     }
05578 
05579     if(adjustment > 0)
05580       curline.insert(col_, spaces_to_insert);
05581     else
05582     {
05583       size_t line_length = curline.size();
05584 
05585       size_t chars_to_delete = -adjustment;
05586 
05587       if(col_ + chars_to_delete > line_length)
05588         chars_to_delete = line_length - col_;
05589         
05590       curline.erase(col_, chars_to_delete);
05591 
05592     }
05593 
05594     replace_line(i, curline, false);  // don't include undo info, we've done it alreay
05595 
05596   }
05597 
05598   paint_page(vp,v);
05599 
05600   return false;
05601 }
05603 
05604 
05605 
05607 AddMapping join_lines_func(CursorWindow::func_join, &Impl::line_join, Modifying);
05608 
05609 bool
05610 Impl::
05611 line_join(viewport* vp, Viewer*v, input_event const *e)
05612 {
05613   // join this line with the next line.
05614 
05615   if(size() == 0 || cur_ == size() - 1)
05616     return false;  // nothing to do, you can't join the end of the file to this line
05617 
05618   std::string next_line = line_text(cur_ + 1);
05619 
05620   std::string cur_line = line_text(cur_);
05621 
05622   if(next_line.size())
05623   {
05624     // next line is not blank -- remove leading blanks from that
05625     // line  -- there's no point in joining blanks.
05626 
05627     size_t col_tmp = cur_line.size();
05628 
05629     std::string::const_iterator first = next_line.begin(),
05630                                 last  = next_line.end();
05631                                 
05632     while(first != last && *first == ' ')
05633       ++first;
05634 
05635     {
05636       size_t s = first - next_line.begin();
05637 
05638       if(s)
05639         next_line.erase(0, first - next_line.begin());
05640     }
05641 
05642     // at this point, the local copy of the next line of text has
05643     // had leading blanks removed.  Now remove trailing blanks from cur_line
05644 
05645     first = cur_line.begin();
05646     last  = cur_line.end();
05647 
05648     while(first != last)
05649     {
05650       --last;
05651 
05652       if(*last != ' ')
05653       {
05654         ++last;
05655         break;
05656       }
05657     }
05658 
05659     // at this point, last points to the end of the string
05660     // or to the first blank character after the last non-blank
05661     // in cur_line
05662 
05663     {
05664        size_t new_length = (last - cur_line.begin());
05665        
05666        cur_line.erase( new_length, cur_line.size() - new_length);
05667 
05668        col_tmp = new_length;
05669     }
05670 
05671     cur_line += next_line;
05672 
05673     // cur_line is now the final product
05674 
05675     col_ = col_tmp;
05676 
05677   }
05678 
05679   UndoOperation* op = push_undo_stack();
05680 
05681   op->code_    = UndoOperation::break_lines;
05682   op->lineno_ = cur_;
05683 
05684   op->block_.push_back(std::string(lines_[cur_]));
05685   op->block_.push_back(std::string(lines_[cur_+1]));
05686 
05687   erase_line( lines_.find(cur_+1), cur_+1, false);
05688   erase_line( lines_.find(cur_), cur_, false);
05689   lines_.insert(lines_.find(cur_), new std::string(cur_line));
05690 
05691   make_col_visible(vp,v);
05692   make_line_visible(vp,v);
05693   paint_page(vp,v);
05694 
05695   return false;
05696 }
05698 
05700 AddMapping insert_lines_func(CursorWindow::func_insline, &Impl::line_insert, Modifying);
05701 
05702 bool
05703 Impl::
05704 line_insert(viewport* vp, Viewer*v, input_event const *e)
05705 {
05706   if(size() == 0)
05707   {
05708     lines_.insert(lines_.begin(), new std::string(""));
05709   }
05710   else
05711   {
05712     lines_.insert(lines_.find(cur_), new std::string(""));
05713   }
05714 
05715   push_undo_stack();
05716   undo_stack_.front()->code_   = UndoOperation::erase_line;
05717   undo_stack_.front()->lineno_ = cur_;
05718 
05719 
05720   make_line_visible(vp,v);
05721   paint_page(vp,v);
05722 
05723   return false;
05724 }
05726 
05728 
05729 AddMapping convert2edit(CursorWindow::func_to_edit, &Impl::to_edit, NonModifying);
05730 
05731 bool
05732 Impl::
05733 to_edit(viewport* vp, Viewer*v, input_event const *e)
05734   // this function makes the current read only session writeable -- ie converts a 
05735   // view into an edit
05736 {
05737   if(!is_writeable_)
05738   {
05739     is_writeable_ = true;
05740     paint_page(vp,v);
05741   }
05742   else
05743     vp->beep();
05744 
05745   return false;
05746 }
05747 
05749 
05751 
05752 AddMapping reread_edit(CursorWindow::func_reread, &Impl::reread, NonModifying);
05753 
05754 bool
05755 Impl::
05756 reread(viewport* vp, Viewer*v, input_event const *e)
05757   // check to see if the file being edited has changed on disk.
05758 {
05759   // cannot be undone!
05760 
05761   if(!fullname_.exists())
05762   {
05763     // if there's nothing to read, warn the user then quit.
05764 
05765     CursorWindow::Message d("Error reading file " + fullname_);
05766   
05767     d += "";
05768     d += "Press Enter to continue";
05769   
05770     d.popup(manager_->window());
05771   
05772     return false;
05773   }
05774   else
05775   {
05776      if(is_dirty_)
05777      {
05778        vp->beep();
05779 
05780        CursorWindow::Dialog d("Unsaved edit changes exist");
05781 
05782        d += new CursorWindow::Dialog::String("ok",
05783                                              "Discard them? (Yes or No)",
05784                                              "No",
05785                                              4
05786                                             );
05787        
05788        if(d.popup( manager_->window() ))
05789        {
05790          return false;
05791        }
05792        
05793        std::string ok_value = d.element_value("ok");
05794        
05795        if(ok_value.size() > 2)
05796        {
05797          ok_value.erase(2, ok_value.size());
05798        }
05799        
05800        if(ok_value != "Ye" && ok_value != "ye" )
05801        {
05802          vp->beep();
05803          return false;
05804        }
05805      }
05806 
05807      // we are going to reread the file -- so eliminate all the lines currently 
05808      // found -- and the block top and bottom -- 
05809      // and finally go to the top
05810 
05811      file_top(vp, v, e);
05812 
05813      first_mark_ = 0;
05814      last_mark_  = size();
05815 
05816      block_delete(vp, v, e);
05817 
05818      cur_ = 0;
05819 
05820      // size_t s = size();  // should be 0
05821 
05822      // now empty the undo stack
05823 
05824      while(!undo_stack_.empty())
05825      {
05826        delete undo_stack_.front();
05827        undo_stack_.pop_front();
05828      }
05829 
05830      read_file();
05831 
05832      make_line_visible(vp, v);
05833 
05834      paint_page(vp,v);
05835 
05836      return false;
05837 
05838 
05839   }
05840 
05841 
05842   return false;
05843 }
05844 
05846 
05848 
05849 struct is_colon
05850 {
05851   bool operator() (char c) const { return c == ':'; }
05852 };
05853 
05854 struct is_pathsep
05855 {
05856   bool operator() (char c) const { return c == '/' || c == '\\'; }
05857 };
05858 
05859 
05860 AddMapping switch_source_file(CursorWindow::func_switch, 
05861                               &Impl::switch_source, 
05862                               NonModifying);
05863 
05864 bool
05865 Impl::
05866 switch_source(viewport* vp, Viewer*v, input_event const *e)
05867   // switch to an alternate file based on the current filename extension
05868   // and the directories listed in the SOURCEPATH and CDPATH environment
05869   // variables.  The filename extensions of interest are specified in
05870   // environment variable SOURCEALTS like this:
05871   //
05872   //   .c:.h:.:.inl:
05873   //
05874   // A single ., with no other lettering means that 'no extension' is 
05875   // meant -- such as the new c++ standard for system headers.
05876 {
05877   StrTool::stringlist_t dirs;
05878 
05879   //
05880   // Collected needed environment variables and handle errors
05881   //
05882 
05883   std::string srcalts= ProgramOptions::global_options_->getenv("SOURCEALTS");
05884   std::string srcpath= ProgramOptions::global_options_->getenv("SOURCEPATH");
05885   std::string cdpath = ProgramOptions::global_options_->getenv("CDPATH");
05886 
05887 
05888   if(srcalts.size() == 0)
05889   {
05890     vp->beep();
05891 
05892     CursorWindow::Message d("SOURCEALTS environment variable is not set");
05893 
05894     d += "";
05895     d += "Set the SOURCEALTS environment variable to a colon";
05896     d += "separated list of file name extensions which count";
05897     d += "as source files";
05898     d += "";
05899     d += "For example:";
05900     d += "  SRCALTS=:.c:.h:.:.inl";
05901     d += "This example makes the following file name";
05902     d += "extensions valid source alternatives:";
05903     d += "  .c, .h, none, .inl";
05904     d += "";
05905     d += "Press Enter to continue";
05906     
05907     d.popup(manager_->window());
05908     
05909     return false;
05910   }
05911 
05912 
05913   if(srcpath.size() == 0 && cdpath.size() == 0)
05914   {
05915     vp->beep();
05916 
05917     CursorWindow::Message d("No SOURCEPATH or CDPATH set");
05918     
05919     d += "Please set one or both of the following";
05920     d += "environment variables:";
05921     d += "  SOURCEPATH";
05922     d += "  CDPATH";
05923     d += "Both variables are ':' separated lists";
05924     d += "directory names.";
05925     d += "";
05926     d += "Press Enter to continue";
05927     
05928     
05929     d.popup(manager_->window());
05930     
05931     return false;
05932   }
05933 
05934   //
05935   // Environent variables are good -- use them
05936   //
05937 
05938   StrTool::stringlist_t src_types;  // "srcalts split up (.c, .h, etc)"
05939   StrTool::stringlist_t src_dirs;   // "srcpath split up" + cdpath split up
05940   StrTool::stringlist_t src_path;   // "srcpath split up"
05941   StrTool::stringlist_t cd_dirs;    // "srcpath split up"
05942 
05943   src_dirs.push_back(".");
05944 
05945   StrTool::parse_words(srcalts, &src_types, 20000, is_colon());
05946   StrTool::parse_words(srcpath, &src_path,  20000, is_colon());
05947   StrTool::parse_words(cdpath,  &cd_dirs,  20000, is_colon());
05948 
05949   StrTool::stringlist_t::iterator sbegin, send;
05950 
05951   //
05952   // expand any directory name patters in the last basename's of src_path
05953   // elements
05954   //
05955 
05956   for(sbegin = src_path.begin(), send = src_path.end();
05957       sbegin != send;
05958       ++sbegin
05959      )
05960   {
05961     FileName cur(*sbegin);
05962 
05963     if(!cur.is_absolute_path())
05964     {
05965       cur = fullname_.dirname() + cur;
05966     }
05967 
05968     if(cur.basename() == "*")
05969     {
05970       FileName::sorted_names_t tmp;
05971 
05972       FileName::find_matching(cur, &tmp, 0x03);  
05973         // get directories only and full paths
05974 
05975       FileName::sorted_names_t::iterator a=tmp.begin(), b=tmp.end();
05976       while(a != b)
05977       {
05978         src_dirs.push_back(*a++);
05979       }
05980 
05981     }
05982     else
05983     {
05984       src_dirs.push_back(cur);
05985     }
05986   }
05987 
05988   //
05989   // include the cdpath in the src_dirs
05990   //
05991   for(sbegin = cd_dirs.begin(), send = cd_dirs.end();
05992       sbegin != send;
05993       ++sbegin
05994      )
05995   {
05996     src_dirs.push_back(*sbegin);
05997   }
05998 
05999 
06000   //
06001   // handle the 'none' file name extension
06002   //
06003 
06004   sbegin =  src_types.begin();
06005   send   =  src_types.end();
06006 
06007   while(sbegin != send)
06008   {
06009     if(*sbegin == ".")
06010       *sbegin = "";
06011 
06012     ++sbegin;
06013   }
06014 
06015 
06016   //
06017   //  Verify that this file is described by SOURCEALTS
06018   //
06019 
06020   std::string ext = fullname_.extension();
06021   std::string base= fullname_.basename(true);
06022 
06023 
06024   sbegin =  src_types.begin();
06025   send   =  src_types.end();
06026 
06027   while(sbegin != send)
06028   {
06029     if(ext == *sbegin)
06030       break;
06031 
06032     ++sbegin;
06033   }
06034 
06035   if(sbegin == send)
06036   {
06037     vp->beep();
06038 
06039     std::string tmp("  "); tmp += srcalts;
06040 
06041     CursorWindow::Message d("This file is not listed in SOURCEALTS");
06042 
06043     d += "In order to use the 'switch_source' editor function,";
06044     d += "you must be editing a file whose file name extension";
06045     d += "is listed in the environment variable, SOURCEALTS.";
06046     d += "";
06047     d += "That variable contains the following: ";
06048     d += tmp;
06049     d += "";
06050     d += "But you are editing a file whose extension is '" + ext + "'";
06051     d += "";
06052     d += "Press Enter to continue";
06053 
06054     d.popup(manager_->window());
06055     
06056     return false;
06057   }
06058 
06059   //
06060   // Find files matching the requirements
06061   //
06062 
06063   {
06064      typedef std::set<std::string> matches_t;
06065 
06066      matches_t matches;
06067 
06068      for(sbegin  =src_dirs.begin(),
06069            send  =src_dirs.end();
06070          sbegin != send;
06071          ++sbegin
06072         )
06073      {
06074         StrTool::stringlist_t::iterator f = src_types.begin(),
06075                                         l = src_types.end();
06076         while(f != l)
06077         {
06078           std::string cur_ext = *f;
06079 
06080           FileName test(*sbegin + "/" + base + cur_ext);
06081 
06082           if(test.exists() && !test.is_dir())
06083           {
06084             test.convert_to_absolute_path();
06085             matches.insert(test);
06086           }
06087 
06088           ++f;
06089 
06090         }
06091      }
06092 
06093      //
06094      // If less than 2 matches found, let user see the list of
06095      // source dirs
06096      //
06097 
06098      if(matches.size() < 2)
06099      {
06100        CursorWindow::Message d("FYI:  less than two matches found in the following dirs:");
06101 
06102        for(sbegin = src_dirs.begin(), send = src_dirs.end();
06103            sbegin != send;
06104            ++sbegin
06105           )
06106        {
06107          d += std::string("  ") + *sbegin;
06108        }
06109 
06110        d += "Press enter to continue";
06111      
06112        d.popup(manager_->window());
06113      }
06114 
06115 
06116 
06117      //
06118      //  Prompt the user with the list and let him select one
06119      //
06120 
06121 
06122      {
06123       CursorWindow::Selection d("Select an alternate source file below:");
06124 
06125       matches_t::iterator sbegin, send;
06126 
06127       for(sbegin = matches.begin(), send = matches.end();
06128           sbegin != send;
06129           ++sbegin
06130          )
06131       {
06132         d += *sbegin;
06133       }
06134   
06135       if(d.popup(manager_->window()))
06136         {
06137           return false;
06138         }
06139 
06140       //
06141       // Load the selected file into an editor
06142       // 
06143 
06144       {
06145         FileName file(d.selection_);
06146 
06147         Viewer* oldie = manager_->find_viewer(file);
06148       
06149         if(oldie)
06150         {
06151           manager_->activate(oldie);
06152         }
06153         else
06154         {
06155       
06156           Viewer* newbie = manager_->edit(file);
06157       
06158           if(newbie)
06159           {
06160             manager_->add(newbie, file);
06161       
06162           }
06163           else
06164             vp->beep();
06165         }
06166       
06167       }
06168       
06169      }
06170   }
06171 
06172 
06173   return false;
06174 }
06176 
06177 bool
06178 Impl::
06179 fetchAllLines(std::list< std::string > &output)
06180 {
06181     rep_type::iterator first = lines_.begin(), last = lines_.end();
06182 
06183     while(first != last)
06184     {
06185         output.push_back(*first++);
06186     }
06187 
06188     return true;
06189 }
06190 
Generated on Wed Feb 29 22:50:03 2012 for CXXUtilities by  doxygen 1.6.3