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
00027
00028
00029
00034 #include <cxxtls/ftpviewer.h>
00035 #include <cxxtls/ftp.h>
00036 #include <cxxtls/viewermanager.h>
00037 #include <vector>
00038 #include <list>
00039 #include <cxxtls/file.h>
00040 #include <portable_io.h>
00041 #include <cxxtls/strtool.h>
00042 #include <cxxtls/cursesinterface.h>
00043
00044 namespace cxxtls
00045 {
00046
00047
00048
00049 typedef CursorWindow::viewport viewport;
00050 typedef CursorWindow::input_event input_event;
00051 typedef std::string string;
00052 typedef std::vector<FTP::FileInfo> dir_rep;
00053 typedef CursorWindow::Dialog dialog;
00054 typedef dialog::String string_field;
00055 typedef dialog::Password password_field;
00056 typedef dialog::Ok ok_field;
00057 typedef FTP::FileInfo file_info;
00058 typedef CursorWindow::Message message;
00059 typedef CursorWindow::Selection selection;
00060 typedef FileName::sorted_names_t filename_list;
00061 typedef std::list<file_info> fileinfo_list;
00062 typedef std::map<string, FTPviewer*> viewer_map;
00063
00064
00066 struct FTPviewer::Impl
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077 {
00078 void paint (viewport* vp, int cmd);
00079 bool handle_event (input_event const *e, viewport* vp);
00080 void help();
00081 void paint_row (viewport*vp, int row, size_t line, bool show_highlights=true);
00082
00083 static viewer_map active_viewers_;
00084
00085 ViewerManager* manager_;
00086 FileName local_directory_;
00087 FileName remote_directory_;
00088 string remote_host_;
00089 string userid_;
00090 string password_;
00091 dir_rep lines_;
00092 bool is_active_;
00093 size_t page_top_;
00094 size_t cur_line_;
00095 int first_row_;
00096 int visible_rows_;
00097 string search_;
00098 int left_margin_;
00099 };
00100
00102 viewer_map FTPviewer::Impl::active_viewers_;
00103
00104
00106 void
00107 FTPviewer::
00108 operator() (viewport* vp, int cmd)
00109 {
00110
00111
00112 impl_->paint(vp, cmd);
00113 }
00114
00116 bool
00117 FTPviewer::
00118 handle_event(input_event const * e,viewport* vp)
00119 {
00120 return impl_->handle_event(e,vp);
00121 }
00122
00124 void
00125 FTPviewer::
00126 help()
00127 {
00128 impl_->help();
00129 }
00130
00132 string
00133 FTPviewer::
00134 app_name("FTP");
00135
00137 FTPviewer::
00138 FTPviewer(ViewerManager * vm,
00139 FileName const& local_directory,
00140 string const& remote_host,
00141 string const& userid,
00142 string const& password,
00143 string const& remote_directory
00144 )
00145 : Viewer(vm),
00146 impl_(new Impl)
00147 {
00148 impl_->active_viewers_[remote_directory] = this;
00149
00150 impl_->local_directory_ = local_directory;
00151 impl_->remote_host_ = remote_host;
00152 impl_->userid_ = userid;
00153 impl_->password_ = password;
00154 impl_->remote_directory_= remote_directory;
00155 impl_->is_active_ = false;
00156 impl_->first_row_ = 1;
00157 impl_->page_top_ = 0;
00158 impl_->cur_line_ = 0;
00159 impl_->visible_rows_ = 0;
00160 impl_->manager_ = vm;
00161 impl_->left_margin_ = 0;
00162
00163 }
00164
00166 FTPviewer::
00167 ~FTPviewer()
00168 {
00169 viewer_map::iterator victim =
00170 impl_->active_viewers_.find(impl_->remote_directory_);
00171
00172 if(victim != impl_->active_viewers_.end())
00173 {
00174 impl_->active_viewers_.erase(victim);
00175 }
00176
00177 }
00179
00180 string
00181 FTPviewer::
00182 description() const
00183 {
00184 return impl_->remote_host_ + ":" + impl_->remote_directory_;
00185 }
00186
00188 Viewer*
00189 FTPviewer::
00190 app(ViewerManager*vm, string &fullname)
00191 {
00192
00193
00194 string host;
00195 string user;
00196 string pwd;
00197 FileName localdir(FileName::getcwd());
00198
00199 if(fullname != "")
00200 {
00201
00202
00203 StrTool::stringlist_t parts;
00204
00205 StrTool::parse_words(fullname, &parts);
00206
00207 size_t count = parts.size();
00208
00209 if(count >= 1)
00210 {
00211 host = parts.front();
00212 --count;
00213 parts.erase(parts.begin());
00214 }
00215
00216 if(count >= 1)
00217 {
00218 user = parts.front();
00219 --count;
00220 parts.erase(parts.begin());
00221 }
00222
00223 if(count >= 1)
00224 {
00225 pwd = parts.front();
00226 --count;
00227 parts.erase(parts.begin());
00228 }
00229
00230 if(count >= 1)
00231 {
00232 localdir = parts.front();
00233 --count;
00234 parts.erase(parts.begin());
00235 }
00236
00237 fullname="";
00238
00239 }
00240
00241
00242
00243 if(pwd == "")
00244 {
00245
00246
00247 dialog d("FTP connection");
00248 d += new string_field("host", "Host", host, 20);
00249 d += new string_field("user", "User", user, 20);
00250 d += new password_field("password", "Password", pwd, 20);
00251 d += new string_field("localdir", "Local Directory", localdir, 40);
00252 d += new ok_field("ok", "Ok");
00253
00254 if(d.popup(vm->window()))
00255 return 0;
00256
00257 host = d.element_value("host");
00258 user = d.element_value("user");
00259 pwd = d.element_value("password");
00260 localdir = d.element_value("localdir");
00261 }
00262
00263
00264 FTPviewer* rv = new FTPviewer(vm, fullname=localdir, host, user, pwd);
00265
00266 fileinfo_list lofi;
00267
00268 int count = FTP::stat_matching(host, user, pwd, "", &lofi);
00269
00270 if(count < 0)
00271 {
00272 message m("Couldn't connect to remote server");
00273 m += "Ftp error was: ";
00274 m += " " + FTP::error();
00275 m += "";
00276
00277 m.popup(vm->window());
00278
00279 delete rv;
00280 return 0;
00281 }
00282
00283 rv->impl_->lines_.resize(count);
00284
00285 std::copy(lofi.begin(), lofi.end(), rv->impl_->lines_.begin());
00286
00287 return rv;
00288
00289 }
00291 void
00292 FTPviewer::Impl::
00293 paint_row (viewport*vp, int row, size_t line, bool show_highlights)
00294 {
00295
00296
00297
00298
00299
00300
00301
00302
00303 if(line != cur_line_ || !show_highlights)
00304 {
00305 if(row == visible_rows_ - 1)
00306 vp->set_text_attribute(ViewerManager::bottom_att);
00307 else
00308 vp->set_text_attribute(ViewerManager::normal_att);
00309 }
00310 else
00311 vp->set_text_attribute(ViewerManager::highlighted_att);
00312
00313
00314
00315
00316
00317
00318
00319 vp->set_curpos(row + first_row_, left_margin_);
00320
00321
00322
00323
00324
00325
00326 if(line < lines_.size())
00327 {
00328
00329
00330
00331
00332
00333
00334
00335 file_info &info = lines_[line];
00336
00337 FileStatus& status = info.status_;
00338
00339 string line_text;
00340
00341 int cols = vp->size().col_;
00342
00343 if(cols > 30)
00344 {
00345 if(cols >= 69)
00346 {
00347 line_text += status.mode;
00348 line_text += " ";
00349 }
00350
00351 if(cols >= 60)
00352 {
00353
00354 line_text += status.time;
00355 line_text += " ";
00356 }
00357
00358 if(cols >= 42 )
00359 {
00360 char buffer[40];
00361
00362 sprintf(buffer, "%12lu", (unsigned long)status.size);
00363
00364 line_text += buffer;
00365 }
00366
00367 line_text += " ";
00368 }
00369
00370 line_text += info.name_;
00371
00372 vp->write(line_text);
00373
00374 }
00375
00376 vp->fill_to_eol();
00377 }
00379 void
00380 FTPviewer::Impl::
00381 paint(viewport* vp, int cmd)
00382 {
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393 if(cmd == viewport::repaint_handler::activate)
00394 is_active_ = true;
00395 else
00396 if(cmd == viewport::repaint_handler::deactivate)
00397 is_active_ = false;
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410 row_col size = vp->size();
00411 row_col origin = vp->origin();
00412
00413 if(origin.col_ == 0)
00414 left_margin_ = 0;
00415 else
00416 left_margin_ = 1;
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428 vp->set_curpos(0,0);
00429
00430 if(left_margin_)
00431 {
00432 vp->set_text_attribute(ViewerManager::inactive_mark_att);
00433
00434 using namespace CursesInterface;
00435
00436 vp->write(line_chars[VL_MIDDLE]);
00437 }
00438
00439 if(is_active_)
00440 vp->set_text_attribute(ViewerManager::active_title_att);
00441 else
00442 vp->set_text_attribute(ViewerManager::inactive_title_att);
00443
00444 *vp << "FTP("
00445 << remote_host_
00446 << "): "
00447 << remote_directory_.shorten(size.col_ - 6 - remote_host_.size())
00448 ;
00449
00450 vp->fill_to_eol();
00451
00452
00453
00454
00455
00456 visible_rows_ = size.row_- first_row_;
00457
00458 int row;
00459
00460 for(row=0; row < visible_rows_; ++row)
00461 {
00462 size_t line = row + page_top_;
00463
00464 if(line >= lines_.size())
00465 break;
00466
00467 paint_row(vp, row, line);
00468
00469 if(left_margin_)
00470 {
00471 vp->set_curpos(row + first_row_, 0);
00472 vp->set_text_attribute(ViewerManager::inactive_mark_att);
00473
00474 using namespace CursesInterface;
00475
00476 vp->write(line_chars[VL_MIDDLE]);
00477 }
00478
00479 }
00480
00481
00482
00483
00484
00485 vp->set_text_attribute(ViewerManager::normal_att);
00486
00487 while(row < visible_rows_)
00488 {
00489 vp->set_curpos(row + first_row_, 0);
00490
00491 if(row == visible_rows_ - 1)
00492 vp->set_text_attribute(ViewerManager::bottom_att);
00493
00494 vp->fill_to_eol();
00495
00496 if(left_margin_)
00497 {
00498 vp->set_curpos(row + first_row_, 0);
00499 vp->set_text_attribute(ViewerManager::inactive_mark_att);
00500
00501 using namespace CursesInterface;
00502
00503 vp->write(line_chars[VL_MIDDLE]);
00504 }
00505
00506 ++row;
00507 }
00508
00509
00510
00511
00512
00513 vp->set_curpos(cur_line_ - page_top_ + first_row_, left_margin_);
00514
00515 }
00517 bool
00518 FTPviewer::Impl::
00519 handle_event (input_event const *e, viewport* vp)
00520 {
00521
00522 if(e->type_ == input_event::ForceExitKey)
00523 return true;
00524
00525
00526 if(e->type_ != input_event::DataKey &&
00527 e->type_ != input_event::FunctionKey
00528 )
00529 return false;
00530
00531
00532 if(lines_.size() == 0 &&
00533 e->value_ != 'p' &&
00534 e->value_ != '^'
00535 )
00536 return true;
00537
00538
00539
00540 int edit_func = vp->window()->func[e->value_];
00541
00542 paint_row(vp, cur_line_ - page_top_, cur_line_, false);
00543
00544
00545
00546 switch(edit_func)
00547 {
00548 case CursorWindow::func_data:
00549 switch(e->value_)
00550 {
00551 case 'q':
00552 case 'k':
00553 return true;
00554
00555 case 'd':
00556 {
00557 std::string child_path;
00558
00559 child_path = remote_directory_;
00560
00561 if(child_path.size())
00562 child_path += "/";
00563
00564 FileName curfile = child_path + lines_[cur_line_].name_;
00565
00566 if(lines_[cur_line_].status_.mode.is_dir())
00567 {
00568 vp->beep();
00569 break;
00570 }
00571
00572 {
00573 dialog d("Delete file (Esc means NO!)");
00574
00575 d += new ok_field("ok", string("Delete") + lines_[cur_line_].name_);
00576
00577 if(d.popup(manager_->window()))
00578 {
00579 break;
00580 }
00581
00582 }
00583
00584 int err = FTP::del(remote_host_,
00585 userid_,
00586 password_,
00587 curfile
00588 );
00589
00590 if(err < 0)
00591 {
00592 vp->beep();
00593 }
00594 else
00595 {
00596 lines_.erase(lines_.begin() + cur_line_);
00597 vp->activate();
00598 }
00599
00600 }
00601 break;
00602
00603 case 'g':
00604 {
00605
00606 std::string child_path;
00607
00608 child_path = remote_directory_;
00609
00610 if(child_path.size())
00611 child_path += "/";
00612
00613 FileName curfile = child_path + lines_[cur_line_].name_;
00614
00615 if(lines_[cur_line_].status_.mode.is_dir())
00616 {
00617 vp->beep();
00618 break;
00619 }
00620
00621 {
00622 dialog d("Download " + lines_[cur_line_].name_ + " (esc for no)");
00623
00624 d += new ok_field("ok", "ok");
00625
00626 if(d.popup(manager_->window()))
00627 {
00628 break;
00629 }
00630 }
00631
00632
00633 if(FTP::get(remote_host_,
00634 userid_,
00635 password_,
00636 curfile,
00637 local_directory_ + "/" + lines_[cur_line_].name_
00638 )
00639 )
00640 {
00641 message m("Error downloading " + curfile);
00642 m.popup(manager_->window());
00643 }
00644 else
00645 {
00646 manager_->window()->beep();
00647 message m("Download successfull!");
00648 m.popup(manager_->window());
00649
00650 }
00651
00652 }
00653 break;
00654
00655 case 'p':
00656 {
00657 filename_list filenames;
00658
00659 int count = FileName::find_matching(local_directory_ + "/*",
00660 &filenames);
00661
00662 if(count)
00663 {
00664 selection s("Select a file to upload:");
00665
00666 filename_list::iterator first = filenames.begin(),
00667 last = filenames.end();
00668
00669 while(first != last)
00670 {
00671 string const& cur = *first++;
00672
00673 s += cur;
00674 }
00675
00676
00677 if(!s.popup(manager_->window()))
00678 {
00679 int rc = FTP::put(remote_host_,
00680 userid_,
00681 password_,
00682 remote_directory_ + "/" +s.selection_
00683 );
00684
00685 if(rc < 0)
00686 {
00687 message m("Error putting file");
00688
00689 m += " " + FTP::error();
00690
00691 m.popup( manager_->window() );
00692 }
00693 else
00694 {
00695 fileinfo_list tmp;
00696
00697 int count = FTP::stat_matching(remote_host_,
00698 userid_,
00699 password_,
00700 remote_directory_ + "/*",
00701 &tmp
00702 );
00703
00704 if(count < 0)
00705 {
00706 message m("Error: couldn't re-read remote directory");
00707 m += "";
00708 m += " " + FTP::error();
00709
00710 m.popup(manager_->window());
00711 }
00712 else
00713 {
00714 lines_.resize(count);
00715
00716 std::copy(tmp.begin(), tmp.end(), lines_.begin());
00717 vp->activate();
00718
00719 }
00720
00721 }
00722 }
00723
00724 }
00725 else
00726 {
00727 message m("No files in local directory");
00728 m += "";
00729 m += " sorry...";
00730
00731 m.popup(manager_->window());
00732 }
00733
00734
00735 }
00736 break;
00737
00738 case '^':
00739 {
00740 if(remote_directory_.size() == 0)
00741 {
00742 vp->beep();
00743 break;
00744 }
00745
00746 FileName curfile = remote_directory_.dirname();
00747
00748 if(curfile[curfile.size()-1] == '/')
00749 {
00750 curfile.erase(curfile.size()-1);
00751 }
00752
00753 Viewer*parent_ftp_session = active_viewers_[curfile];
00754
00755 if(parent_ftp_session)
00756 {
00757 manager_->activate(parent_ftp_session);
00758 return false;
00759 }
00760
00761 {
00762
00763
00764 FTPviewer* newbie = new FTPviewer(manager_,
00765 local_directory_,
00766 remote_host_,
00767 userid_,
00768 password_,
00769 curfile);
00770
00771 if(newbie)
00772 {
00773 fileinfo_list lofi;
00774
00775 int count = FTP::stat_matching(remote_host_,
00776 userid_,
00777 password_,
00778 curfile + "/*",
00779 &lofi
00780 );
00781
00782
00783
00784 if(count < 0)
00785 {
00786 message m("FTP operation failed");
00787
00788 m += " " + FTP::error();
00789
00790 m.popup(manager_->window());
00791 delete newbie;
00792 }
00793 else
00794 {
00795 newbie->impl_->lines_.resize(count);
00796
00797 std::copy(lofi.begin(), lofi.end(), newbie->impl_->lines_.begin());
00798
00799 manager_->add(newbie, local_directory_);
00800
00801 return false;
00802
00803 }
00804 }
00805 }
00806 }
00807 break;
00808
00809
00810 default:
00811 vp->beep();
00812 break;
00813 }
00814 break;
00815
00816 case CursorWindow::func_find:
00817 {
00818 dialog d("Find file or directory");
00819
00820 d += new string_field("string", "Search for", "", 20);
00821 d += new ok_field("ok", "ok");
00822
00823 if(d.popup(manager_->window()))
00824 {
00825 break;
00826 }
00827
00828 search_ = d.element_value("string");
00829
00830 size_t scan = cur_line_+1;
00831
00832 while(scan < lines_.size())
00833 {
00834
00835 string::iterator start = std::search(lines_[scan].name_.begin(),
00836 lines_[scan].name_.end(),
00837 search_.begin(),
00838 search_.end()
00839 );
00840 if(start != lines_[scan].name_.end())
00841 {
00842
00843
00844 break;
00845 }
00846
00847 ++scan;
00848 }
00849
00850 if(scan == lines_.size())
00851 {
00852 vp->beep();
00853 }
00854 else
00855 {
00856 page_top_ = scan;
00857 cur_line_ = scan;
00858 vp->activate();
00859 }
00860 }
00861 break;
00862
00863 case CursorWindow::func_findnxt:
00864 {
00865 size_t scan = cur_line_+1;
00866
00867 while(scan < lines_.size())
00868 {
00869
00870 string::iterator start = std::search(lines_[scan].name_.begin(),
00871 lines_[scan].name_.end(),
00872 search_.begin(),
00873 search_.end()
00874 );
00875 if(start != lines_[scan].name_.end())
00876 {
00877
00878
00879 break;
00880 }
00881
00882 ++scan;
00883 }
00884
00885 if(scan == lines_.size())
00886 {
00887 vp->beep();
00888 }
00889 else
00890 {
00891 page_top_ = scan;
00892 cur_line_ = scan;
00893 vp->activate();
00894 }
00895 }
00896 break;
00897
00898 case CursorWindow::func_clreol:
00899 return true;
00900
00901 case CursorWindow::func_enter:
00902 {
00903 std::string child_path;
00904
00905 child_path = remote_directory_;
00906
00907 if(child_path.size())
00908 child_path += "/";
00909
00910 FileName curfile = child_path + lines_[cur_line_].name_;
00911
00912 if(lines_[cur_line_].status_.mode.is_dir())
00913 {
00914
00915
00916 Viewer* child_ftp_session = active_viewers_[curfile];
00917
00918 if(child_ftp_session)
00919 {
00920 manager_->activate(child_ftp_session);
00921 return false;
00922 }
00923
00924
00925 FTPviewer* newbie = new FTPviewer(manager_,
00926 local_directory_,
00927 remote_host_,
00928 userid_,
00929 password_,
00930 curfile);
00931
00932 if(newbie)
00933 {
00934 fileinfo_list lofi;
00935
00936 int count = FTP::stat_matching(remote_host_,
00937 userid_,
00938 password_,
00939 curfile + "/*",
00940 &lofi
00941 );
00942
00943
00944
00945 if(count < 0)
00946 {
00947 message m("FTP operation failed");
00948
00949 m += " " + FTP::error();
00950
00951 m.popup(manager_->window());
00952 delete newbie;
00953 }
00954 else
00955 {
00956 newbie->impl_->lines_.resize(count);
00957
00958 std::copy(lofi.begin(), lofi.end(), newbie->impl_->lines_.begin());
00959
00960 manager_->add(newbie, local_directory_);
00961
00962 return false;
00963
00964 }
00965 }
00966 }
00967 else
00968 {
00969
00970
00971 int count = FTP::get(remote_host_,
00972 userid_,
00973 password_,
00974 curfile,
00975 "ftp.tmp"
00976 );
00977
00978 if(count < 0)
00979 {
00980 message m("Error downloading file for viewing");
00981
00982 m += string("Remote file name = ") + lines_[cur_line_].name_;
00983 m += "";
00984 m += "Local file name = ftp.tmp";
00985 m += "";
00986 m += string("Error = ") + FTP::error();
00987
00988 m.popup( manager_->window() );
00989
00990 }
00991 else
00992 {
00993 Viewer *v = manager_->open("ftp.tmp");
00994
00995 if(v)
00996 {
00997 manager_->add(v, string("ftp://") + remote_host_ + "/" + curfile);
00998 return false;
00999 }
01000 }
01001
01002
01003
01004 }
01005
01006 }
01007 break;
01008
01009 case CursorWindow::func_top:
01010 page_top_ = 0;
01011 cur_line_ = 0;
01012 vp->activate();
01013 break;
01014
01015 case CursorWindow::func_bottom:
01016 if(lines_.size())
01017 {
01018 page_top_ = lines_.size()-1;
01019 cur_line_ = lines_.size()-1;
01020 }
01021 else
01022 {
01023 page_top_ = 0;
01024 cur_line_ = 0;
01025 }
01026 vp->activate();
01027 break;
01028
01029 case CursorWindow::func_down:
01030
01031 if(cur_line_ < lines_.size() -1)
01032 {
01033 ++cur_line_;
01034 }
01035
01036 if(cur_line_ - page_top_ >= size_t(visible_rows_))
01037 {
01038 ++page_top_;
01039
01040 vp->activate();
01041 }
01042 break;
01043
01044 case CursorWindow::func_up:
01045
01046 if(cur_line_)
01047 {
01048 --cur_line_;
01049
01050 if(cur_line_ < page_top_ )
01051 {
01052 --page_top_;
01053
01054 vp->activate();
01055 }
01056 }
01057 break;
01058
01059 case CursorWindow::func_next:
01060
01061 cur_line_ += visible_rows_;
01062 page_top_ += visible_rows_;
01063
01064 if(cur_line_ >= lines_.size())
01065 {
01066 cur_line_ = lines_.size() - 1;
01067 page_top_ = cur_line_;
01068 }
01069
01070 vp->activate();
01071
01072 break;
01073
01074 case CursorWindow::func_prior:
01075
01076 if(page_top_ < size_t(visible_rows_))
01077 {
01078 page_top_ = 0;
01079 cur_line_ = 0;
01080 }
01081 else
01082 {
01083 page_top_ -= visible_rows_;
01084 cur_line_ -= visible_rows_;
01085 }
01086
01087 vp->activate();
01088 break;
01089 }
01090
01091 paint_row(vp, cur_line_ - page_top_, cur_line_);
01092
01093 vp->set_curpos(first_row_ + (cur_line_ - page_top_), 0);
01094
01095 return false;
01096
01097 }
01099 void
01100 FTPviewer::Impl::
01101 help()
01102 {
01103 typedef std::list<string> help_text;
01104
01105 std::auto_ptr< help_text > viewer_help( new help_text );
01106
01107 viewer_help->push_back("Help for the FTP viewer");
01108
01109 viewer_help->push_back(" In addition to the standard key bindings");
01110 viewer_help->push_back(" shown later, the text editor interprets");
01111 viewer_help->push_back(" the following parochial key bindings:");
01112 viewer_help->push_back(" ^X ^K -- quits the edit session");
01113 viewer_help->push_back(" ^X k -- quits the edit session");
01114 viewer_help->push_back("");
01115 viewer_help->push_back(" 'g' -- downloads the file under the cursor");
01116 viewer_help->push_back(" 'p' -- uploads a file from the local directory");
01117 viewer_help->push_back(" 'd' -- deletes the file under the cursor");
01118 viewer_help->push_back(" Enter -- navigates down into sub-directories");
01119 viewer_help->push_back(" '^' -- navigate up to the parent directory");
01120 viewer_help->push_back(" ^S -- search for file or directory name fragments");
01121 viewer_help->push_back(" ^N -- repeat previous search");
01122 viewer_help->push_back("");
01123
01124
01125 manager_->help_helper(*viewer_help);
01126 }
01127
01128 }
01129