00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
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
00177
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;
00230
00231
00232 struct TextIterator
00244 {
00245 int line_;
00246 std::string text_;
00247 size_t length_;
00248 size_t col_;
00249 rep_type& rep_;
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
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()
00289 {
00290 if(col_ > length_)
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()
00312 {
00313 if(col_ >= length_)
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_;
00330
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,
00402 insert_block,
00403 delete_block,
00404 replace_block,
00405 break_lines,
00406 nested_undo_ops,
00407
00408
00409
00410
00411 last_code
00412 };
00413
00414 int code_;
00415 int count_;
00416
00417 std::string text_;
00418
00419 int lineno_;
00420
00421 std::list<std::string> block_;
00422
00423
00424 typedef std::list<UndoOperation*> undo_stack;
00425
00426 undo_stack *nested_undo_stack_;
00427
00428 int nested_first_mark_;
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
00451
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_;
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
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
00579
00580
00581 UndoOperation* op = undo_stack_.front();
00582
00583 if(op->code_ != UndoOperation::replace_line ||
00584 op->lineno_ != line
00585 )
00586 {
00587
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
00734
00735
00736
00737 TextIterator top_pointer();
00738 TextIterator cur_pointer();
00739 TextIterator bot_pointer();
00740
00741
00742
00743
00744
00745
00746 bool mixed_case_;
00747 bool match_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
00779
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 *);
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 *);
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 *);
00831 bool switch_source (viewport*, Viewer*, input_event const *);
00832
00833 };
00834
00835 typedef TextEditor::Impl Impl;
00836
00837
00838 std::map<int, std::pair<Impl::func_handler, bool> > Impl::func;
00839
00840
00841
00842 const bool Modifying= true;
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
00884
00885
00886
00887 row_col size = vp->size();
00888 row_col origin = vp->origin();
00889
00890 int avail_rows = impl_->vheight(size);
00891
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
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
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
00951
00952 if(impl_->cur_ - impl_->page_top_ >= avail_rows)
00953 {
00954
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 ;
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
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
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
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
01050
01051
01052
01053
01054 impl_->key_kludge_ = false;
01055
01056 if(e->value_ == ('='))
01057 {
01058 impl_->switch_source(vp,this,e);
01059 }
01060 else
01061 if(e->value_ == ('L'-'@'))
01062 {
01063 impl_->reread(vp,this,e);
01064 }
01065 else
01066 if(e->value_ == ('S' - '@'))
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' - '@') ||
01078 e->value_ == 'k' ||
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;
01124 impl_->word_kludge_ = true;
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;
01155 impl_->word_kludge_ = true;
01156 }
01157 else
01158 if(e->value_ == ('I' - '@'))
01159 {
01160 impl_->block_insert(vp,this,e);
01161 }
01162 else
01163 if(e->value_ == ('B' - '@'))
01164 {
01165 impl_->block_adjust(vp,this,e);
01166 }
01167 else
01168 if(e->value_ == ('U' - '@'))
01169 {
01170 impl_->undo(vp,this,e);
01171 }
01172 else
01173 if(e->value_ == '0')
01174 {
01175 impl_->show_symbols(vp,this,e);
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();
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
01235
01236
01237
01238
01239
01240
01241
01242
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;
01369 impl_->use_tabs_ = true;
01370 impl_->bottom_line_ = 10000;
01371 impl_->first_mark_ = -1;
01372 impl_->last_mark_ = -1;
01373 impl_->left_margin_ = 0;
01374 impl_->manager_ = vm;
01375 impl_->key_kludge_ = false;
01376 impl_->word_kludge_ = false;
01377 impl_->mixed_case_ = true;
01378 impl_->match_words_ = false;
01379 impl_->is_writeable_= true;
01380 impl_->is_new_ = false;
01381 impl_->is_regex_ = false;
01382
01383 impl_->read_file();
01384
01385 impl_->is_dirty_ = false;
01386
01387 {
01388
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
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
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
01867 first_mark_ = last_mark_ = -1;
01868 }
01869 else
01870 if(first_mark_ == last_mark_ )
01871 {
01872
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
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
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)
02043 )
02044 {
02045 hasBinary = true;
02046 break;
02047 }
02048 }
02049
02050 }
02051
02052 }
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 }
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
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
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
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;
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
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
02397
02398 if(cur_ >= first_mark_ && cur_ < last_mark_)
02399 {
02400
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
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
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
02485
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
02581
02582 if(cur_)
02583 {
02584
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
02613
02614 --col_;
02615 make_col_visible(vp,v);
02616 restore_cursor(vp);
02617 return false;
02618 }
02619
02620
02621
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;
02712
02713 search_text_ = search_text;
02714
02715 int saved_line = cur_;
02716 size_t saved_col = col_;
02717
02718 --col_;
02719
02720
02721 if(is_regex_ && search_text_[0] == '^')
02722 --cur_;
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
02771
02772 --col_;
02773
02774
02775 if(is_regex_)
02776 {
02777 SimpleRegex *p = ®exp_;
02778
02779 p->SimpleRegex::~SimpleRegex();
02780
02781 new(®exp_) SimpleRegex(search_text_, (mixed_case_ ? "i" : "") );
02782
02783
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 {
02799
02800
02801
02802 std::string curline = line_text(cur_);
02803
02804
02805
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
02819
02820
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
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
02875
02876
02877 if( int(col_) >= 0 )
02878 {
02879
02880
02881
02882
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
02901
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
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
02983
02984 ++col_;
02985
02986
02987 if(is_regex_)
02988 {
02989 SimpleRegex *p = ®exp_;
02990
02991 p->SimpleRegex::~SimpleRegex();
02992
02993 new(®exp_) SimpleRegex(search_text_, (mixed_case_ ? "i" : "") );
02994
02995
02996
02997 if(search_text_[0] == '^')
02998 {
02999 ++cur_;
03000 col_ = 0;
03001 }
03002 }
03003
03004
03005 while(cur_ < lines_.size())
03006 {
03007
03008 std::string curline = line_text(cur_);
03009
03010 if(col_ < curline.size())
03011 {
03012
03013 int startpos = col_;
03014
03015
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
03052
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
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
03114
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_;
03212
03213 if( tolower(dialog.element_value("all")[0]) == 'a')
03214 {
03215
03216
03217 UndoOperation::undo_stack* nested_changes = new UndoOperation::undo_stack;
03218
03219 undo_stack_.swap(*nested_changes);
03220
03221
03222
03223
03224
03225
03226
03227 bool found_one=false;
03228
03229 for(;;)
03230 {
03231 --col_;
03232
03233
03234 if(!replace_next_helper(vp,v,e))
03235 break;
03236
03237 found_one = true;
03238 }
03239
03240 undo_stack_.swap(*nested_changes);
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
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
03272
03273 --col_;
03274
03275
03276 if(replace_next_helper(vp,v,e))
03277 {
03278
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
03306
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
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_;
03365
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
03398 {
03399 if(first_mark_ == last_mark_)
03400 {
03401 vp->beep();
03402 return false;
03403 }
03404
03405
03406
03407
03408
03409 Viewer::paste_buffer_type &paste_buffer = *v->paste_buffer();
03410
03411 paste_buffer.erase(paste_buffer.begin(), paste_buffer.end());
03412
03413
03414
03415 UndoOperation *op = push_undo_stack();
03416
03417 op->code_ = UndoOperation::insert_block;
03418 op->lineno_ = first_mark_;
03419
03420
03421
03422
03423 while(last_mark_ != first_mark_)
03424 {
03425
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);
03439
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
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
03489
03490
03491
03492 buffer = StrTool::pack_tabs(insertedLine->c_str());
03493
03494 insertedLine = &buffer;
03495
03496 }
03497 else
03498 {
03499
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
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
03589 {
03590 if(first_mark_ == last_mark_)
03591 return false;
03592
03593 Viewer::paste_buffer_type &paste_buffer = *v->paste_buffer();
03594
03595 paste_buffer.erase(paste_buffer.begin(), paste_buffer.end());
03596
03597
03598
03599
03600 while(last_mark_ != first_mark_)
03601 {
03602
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();
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
03636 {
03637 if(first_mark_ == last_mark_)
03638 {
03639 vp->beep();
03640
03641 return false;
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
03693 {
03694
03695
03696 if(first_mark_ == last_mark_)
03697 {
03698 vp->beep();
03699 return false;
03700 }
03701
03702
03703 static std::list<std::string> script;
03704
03705 if(script.size() == 0)
03706 {
03707 script.push_back("");
03708 script.push_back("p");
03709 script.push_back("");
03710 }
03711 else
03712 {
03713
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
03790
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
03815
03816 if(d.popup( manager_->window() ))
03817 {
03818 return false;
03819 }
03820
03821
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);
03842
03843
03844
03845
03846
03847
03848
03849 std::list<std::string> inputBlock;
03850
03851
03852
03853
03854 {
03855 UndoOperation *op = push_undo_stack();
03856
03857 op->code_ = UndoOperation::insert_block;
03858 op->lineno_ = first_mark_;
03859
03860
03861
03862
03863 while(last_mark_ != first_mark_)
03864 {
03865
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);
03878
03879
03880
03881
03882 op->block_.push_back(tmp);
03883
03884 erase_line(scan, last_mark_, false);
03885
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
03919
03920
03921
03922 buffer = StrTool::pack_tabs(insertedLine.c_str());
03923
03924 insertedLine = buffer;
03925
03926 }
03927 else
03928 {
03929
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);
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
04078
04079
04080 fwrite("\r\n", 2, 1, f);
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
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
04155
04156 {
04157 bool painting=true;
04158
04159 if(!e)
04160 painting = false;
04161
04162
04163 if(undo_stack_.begin() != undo_stack_.end())
04164 {
04165
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
04289
04290
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);
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);
04377
04378
04379
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
04415
04416
04417
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 {
04427
04428 manager_->window()->beep();
04429
04430 return false;
04431
04432 }
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);
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());
04577
04578
04579 if(!etags || !etags->ok())
04580 {
04581
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());
04662
04663
04664 if(!cpptags)
04665 {
04666
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());
04769
04770 if(!etags || !etags->ok())
04771 {
04772
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
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();
04855 }
04856 else
04857 {
04858
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());
04911
04912 if(!cpptags)
04913 {
04914
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);
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
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();
04998 }
04999 else
05000 {
05001
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
05067
05068
05069 file = loc.filename_;
05070 line = loc.lineNumber_;
05071
05072 }
05073 else
05074 {
05075 EtagsDB *etags=(manager_->default_etags_db());
05076
05077 if(etags && etags->ok())
05078 {
05079 if(etags->find_next(&file, &line, &matching_text))
05080 {
05081
05082 }
05083 else
05084 {
05085
05086
05087
05088
05089 vp->beep();
05090 return false;
05091 }
05092 }
05093 else
05094 {
05095
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
05171
05172 TextIterator top = top_pointer(),
05173 cur = cur_pointer(),
05174 bot = bot_pointer();
05175
05176 if(cur == bot)
05177 {
05178
05179
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
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
05235
05236 if(*cur == '\\')
05237 ++cur;
05238
05239 if(cur != bot)
05240 ++cur;
05241 }
05242
05243 }
05244 else
05245 if(c == '"')
05246 {
05247 ++cur;
05248
05249 while(cur != bot && *cur != '"')
05250 {
05251
05252
05253 if(*cur == '\\')
05254 ++cur;
05255
05256 if(cur != bot)
05257 ++cur;
05258 }
05259
05260 }
05261 else
05262 if(c == '/')
05263 {
05264
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
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
05305
05306
05307
05308
05309 --cur;
05310
05311 while(cur != top)
05312 {
05313 if(*cur != '\'')
05314 --cur;
05315 else
05316 {
05317
05318
05319
05320 --cur;
05321
05322 if(*cur != '\\' )
05323 {
05324 ++cur;
05325
05326 break;
05327 }
05328 else
05329 --cur;
05330 }
05331 }
05332
05333 }
05334 else
05335 if(c == '"')
05336 {
05337
05338
05339
05340
05341
05342
05343 --cur;
05344
05345 while(cur != top)
05346 {
05347 char c = *cur;
05348
05349 (void)(c);
05350
05351 if(*cur != '"')
05352 --cur;
05353 else
05354 {
05355
05356
05357
05358 --cur;
05359
05360 if(*cur != '\\' )
05361 {
05362 ++cur;
05363
05364 break;
05365 }
05366 else
05367 --cur;
05368 }
05369 }
05370
05371 }
05372
05373 if( (cur != top) && (*cur == '\n') )
05374 {
05375
05376
05377 --cur;
05378
05379 TextIterator here(cur);
05380
05381 char c = *cur;
05382
05383
05384
05385 while( (cur != top) && (*cur != '\n') )
05386 {
05387 c = *cur;
05388 --cur;
05389 }
05390
05391
05392
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
05409 }
05410
05411 }
05412 else
05413 ++cur;
05414 }
05415
05416 cur = here;
05417
05418 ++cur;
05419 }
05420
05421
05422 if(cur != top)
05423 --cur;
05424 }
05425 }
05426
05427 if(cur == bot || cur == top)
05428 {
05429
05430
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
05453
05454
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);
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
05521
05522
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);
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
05614
05615 if(size() == 0 || cur_ == size() - 1)
05616 return false;
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
05625
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
05643
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
05660
05661
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
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
05735
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
05758 {
05759
05760
05761 if(!fullname_.exists())
05762 {
05763
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
05808
05809
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
05821
05822
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
05868
05869
05870
05871
05872
05873
05874
05875
05876 {
05877 StrTool::stringlist_t dirs;
05878
05879
05880
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
05936
05937
05938 StrTool::stringlist_t src_types;
05939 StrTool::stringlist_t src_dirs;
05940 StrTool::stringlist_t src_path;
05941 StrTool::stringlist_t cd_dirs;
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
05953
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
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
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
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
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
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
06095
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
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
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