123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104 |
- // Copyright (C) 2003 Mooffie <mooffie@typo.co.il>
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation; either version 2 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- #include <config.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <unistd.h> // chdir
- #include <sys/wait.h> // WEXITSTATUS()
- #include <string.h>
- #include "editor.h"
- #include "menus.h"
- #include "scrollbar.h"
- #include "io.h"
- #include "pathnames.h"
- #include "themes.h"
- #include "utf8.h" // used in show_character_code()
- #include "dbg.h"
- #include "transtbl.h"
- #include "helpbox.h"
- Editor *Editor::global_instance; // for SIGHUP
- #define LOAD_HISTORY 1
- #define SAVEAS_HISTORY 0
- #define SEARCH_HISTORY 2
- #define INSERT_HISTORY 1
- #define CHDIR_HISTORY 4
- #define WRITESELECTION_HISTORY 5
- #define SPELER_CMD_HISTORY 6
- #define SPELER_ENCODING_HISTORY 7
- #define EXTERNALEDITOR_HISTORY 8
- // describe_key() - reads a key event from the keyboard, searches and then
- // prints the description of the correspoding action.
- INTERACTIVE void Editor::describe_key()
- {
- dialog.show_message(_("Describe key:"));
- dialog.immediate_update();
- Event evt;
- get_next_event(evt, dialog.wnd);
- // first look for a corresponding action in Editor's action map.
- // If none was found, look in EditBox's.
- const char *action, *desc = NULL;
- if ((action = get_event_action(evt)))
- desc = get_action_description(action);
- else {
- if ((action = wedit.get_event_action(evt)))
- desc = wedit.get_action_description(action);
- }
- dialog.show_message(desc ? _(desc)
- : _("Sorry, no description for this key"));
- }
- INTERACTIVE void Editor::refresh_and_center()
- {
- wedit.center_line();
- refresh();
- }
- void Editor::refresh(bool soft)
- {
- if (!soft)
- clearok(curscr, TRUE);
- if (spellerwnd)
- spellerwnd->invalidate_view();
- if (scrollbar)
- scrollbar->invalidate_view();
- if (menubar)
- menubar->invalidate_view();
- wedit.invalidate_view();
- status.invalidate_view();
- dialog.invalidate_view();
- update_terminal(soft);
- }
- // show_character_code() - prints the code value and the UTF-8 sequence
- // of the character on which the cursor stands.
- INTERACTIVE void Editor::show_character_code()
- {
- unichar ch = wedit.get_current_char();
-
- // construct a nice utf-8 representation in utf8_c_sync
- char utf8[6], utf8_c_syn[6*4 + 1];
- int nbytes = unicode_to_utf8(utf8, &ch, 1);
- for (int i = 0; i < nbytes; i++)
- sprintf(utf8_c_syn + i*4, "\\x%02X", (unsigned char)utf8[i]);
- dialog.show_message_fmt(_("Unicode value: U+%04lX (decimal: %ld), "
- "UTF-8 sequence: %s"),
- (unsigned long)ch, (unsigned long)ch,
- utf8_c_syn);
- }
- // show_character_info() - prints information from UnicodeData.txt
- // about the characetr on which the cursor stands.
- INTERACTIVE void Editor::show_character_info()
- {
- #define MAX_LINE_LEN 1024
- unichar ch = wedit.get_current_char();
- FILE *fp;
- if ((fp = fopen(get_cfg_filename(USER_UNICODE_DATA_FILE), "r")) != NULL
- || (fp = fopen(get_cfg_filename(SYSTEM_UNICODE_DATA_FILE), "r")) != NULL) {
- char line[MAX_LINE_LEN];
- while (fgets(line, MAX_LINE_LEN, fp)) {
- if (strtol(line, NULL, 16) == (long)ch) {
- line[strlen(line) - 1] = '\0'; // remove LF
- dialog.show_message(line);
- break;
- }
- }
- fclose(fp);
- } else {
- set_last_error(errno);
- show_file_io_error(_("Can't open %s: %s"),
- get_cfg_filename(SYSTEM_UNICODE_DATA_FILE));
- }
- #undef MAX_LINE_LEN
- }
- Editor::Editor()
- : dialog(this),
- status(this, &wedit),
- speller(*this, dialog)
- {
- spellerwnd = NULL;
- wedit.set_error_listener(this);
- global_instance = this;
- set_default_encoding(DEFAULT_FILE_ENCODING);
- set_encoding(get_default_encoding());
- set_filename("");
- set_new(false);
- #ifdef HAVE_CURS_SET
- big_cursor = false;
- #endif
- menubar = create_main_menubar(this, &wedit);
- scrollbar = NULL;
- scrollbar_pos = scrlbrNone;
-
- // The rest is only needed if we're in interactive mode,
- // that is, not in "do_log2vis" mode.
- if (terminal::is_interactive()) {
- layout_windows();
- dialog.show_message_fmt(_("Welcome to %s version %s |"
- "F1=Help, F9 or F10=Menu, ALT-X=Quit"),
- PACKAGE, VERSION);
- TranslationTable tbl;
- if (tbl.load(get_cfg_filename(USER_TRANSTBL_FILE))
- || tbl.load(get_cfg_filename(SYSTEM_TRANSTBL_FILE)))
- wedit.set_translation_table(tbl);
- if (tbl.load(get_cfg_filename(USER_REPRTBL_FILE))
- || tbl.load(get_cfg_filename(SYSTEM_REPRTBL_FILE)))
- wedit.set_repr_table(tbl);
- if (tbl.load(get_cfg_filename(USER_ALTKBDTBL_FILE))
- || tbl.load(get_cfg_filename(SYSTEM_ALTKBDTBL_FILE))) {
- wedit.set_alt_kbd_table(tbl);
- } else {
- show_file_io_error(_("Can't load Hebrew kbd emulation %s: %s"),
- get_cfg_filename(SYSTEM_ALTKBDTBL_FILE));
- }
- }
- }
- // show_file_io_error() - convenience method to print I/O errors.
- void Editor::show_file_io_error(const char *msg, const char *filename)
- {
- u8string errmsg;
- errmsg.cformat(msg, filename, get_last_error());
- if (terminal::is_interactive())
- dialog.show_error_message(errmsg.c_str());
- else
- fatal("%s\n", errmsg.c_str());
- }
- #ifdef HAVE_CURS_SET
- INTERACTIVE void Editor::toggle_big_cursor()
- {
- set_big_cursor(!is_big_cursor());
- }
- #endif
- INTERACTIVE void Editor::toggle_cursor_position_report()
- {
- status.toggle_cursor_position_report();
- }
- bool Editor::is_cursor_position_report() const
- {
- return status.is_cursor_position_report();
- }
- // quit() - interactive command to quit the editor. it first makes sure the
- // user don't want to save the changes.
- INTERACTIVE void Editor::quit()
- {
- if (wedit.is_modified()) {
- bool canceled;
- bool save = dialog.ask_yes_or_no(_("Buffer was modified; save changes?"),
- &canceled);
- if (canceled)
- return;
- if (save) {
- save_file();
- if (wedit.is_modified())
- return;
- }
- }
- finished = true; // signal exec()
- }
- // help() - interactive command to load and show the manual.
- void Editor::show_help_topic(const char *topic)
- {
- HelpBox helpbox(this, wedit);
- helpbox.layout_windows();
- if (helpbox.load_user_manual()) {
- if (topic)
- helpbox.jump_to_topic(topic);
- helpbox.exec();
- refresh();
- }
- }
- INTERACTIVE void Editor::help()
- {
- show_help_topic(NULL);
- }
- void Editor::set_scrollbar_pos(scrollbar_pos_t pos)
- {
- if (terminal::is_interactive()) {
- if (pos == scrlbrNone) {
- delete scrollbar;
- scrollbar = NULL;
- } else {
- if (!scrollbar)
- scrollbar = new Scrollbar();
- wedit.sync_scrollbar(scrollbar);
- }
- scrollbar_pos = pos;
- layout_windows();
- }
- }
- INTERACTIVE void Editor::menu_set_scrollbar_none()
- {
- set_scrollbar_pos(scrlbrNone);
- }
- INTERACTIVE void Editor::menu_set_scrollbar_left()
- {
- set_scrollbar_pos(scrlbrLeft);
- }
- INTERACTIVE void Editor::menu_set_scrollbar_right()
- {
- set_scrollbar_pos(scrlbrRight);
- }
- INTERACTIVE void Editor::menu()
- {
- if (menubar)
- menubar->exec();
- }
- bool Editor::save_file(const char *filename, const char *specified_encoding)
- {
- status.invalidate_view(); // encoding may change, so update the statusline
- unichar offending_char;
- if (!xsave_file(&wedit, filename, specified_encoding,
- get_backup_suffix(), offending_char)) {
- show_file_io_error(_("Saving %s failed: %s"), filename);
- if (offending_char)
- wedit.move_first_char(offending_char);
- return false;
- } else {
- set_filename(filename);
- set_encoding(specified_encoding);
- set_new(false);
- wedit.set_modified(false);
- dialog.show_message(_("Saved OK"));
- return true;
- }
- }
- bool Editor::write_selection_to_file(const char *filename,
- const char *specified_encoding)
- {
- unichar offending_char;
- if (!xsave_file(&wedit, filename, specified_encoding,
- get_backup_suffix(), offending_char, true)) {
- show_file_io_error(_("Saving %s failed: %s"), filename);
- if (offending_char)
- wedit.move_first_char(offending_char);
- return false;
- } else {
- dialog.show_message(_("Written OK"));
- return true;
- }
- }
- INTERACTIVE void Editor::write_selection_to_file()
- {
- u8string qry_filename, qry_encoding;
- if (query_filename(wedit.has_selected_text()
- ? _("Write selection to:")
- : _("Write whole text to:"),
- qry_filename, qry_encoding, WRITESELECTION_HISTORY))
- write_selection_to_file(qry_filename.c_str(),
- qry_encoding.empty() ? get_encoding() : qry_encoding.c_str());
- }
- void Editor::set_encoding(const char *s)
- {
- encoding = u8string(s);
- status.invalidate_view();
- }
- bool Editor::load_file(const char *filename, const char *specified_encoding)
- {
- bool is_new;
- u8string effective_encoding;
- status.invalidate_view();
- set_filename("");
- dialog.show_message(_("Loading..."));
- dialog.immediate_update();
- if (!xload_file(&wedit, filename, specified_encoding,
- get_default_encoding(), effective_encoding, is_new, true)) {
- if (!effective_encoding.empty())
- set_encoding(effective_encoding.c_str());
- set_new(false);
- show_file_io_error(_("Loading %s failed: %s"), filename);
- return false;
- } else {
- if (scrollbar)
- wedit.sync_scrollbar(scrollbar);
- set_filename(filename);
- if (!is_new)
- set_encoding(effective_encoding.c_str());
- else
- set_encoding(specified_encoding ? specified_encoding
- : get_default_encoding());
- set_new(is_new);
- dialog.show_message(_("Loaded OK"));
- if (get_syntax_auto_detection())
- detect_syntax();
- else
- wedit.set_syn_hlt(EditBox::synhltOff);
- return true;
- }
- }
- // insert_file() - insert file at the cursor location.
- bool Editor::insert_file(const char *filename, const char *specified_encoding)
- {
- bool dummy_is_new;
- u8string dummy_effective_encoding;
- status.invalidate_view();
-
- if (!xload_file(&wedit, filename, specified_encoding,
- get_default_encoding(), dummy_effective_encoding,
- dummy_is_new, false)) {
- show_file_io_error(_("Loading %s failed: %s"), filename);
- return false;
- } else {
- dialog.show_message(_("Inserted OK"));
- return true;
- }
- }
- INTERACTIVE void Editor::save_file_as()
- {
- u8string qry_filename = get_filename(), qry_encoding;
- if (query_filename(_("Save file as:"), qry_filename, qry_encoding, SAVEAS_HISTORY))
- save_file(qry_filename.c_str(),
- qry_encoding.empty() ? get_encoding() : qry_encoding.c_str());
- }
- INTERACTIVE void Editor::save_file()
- {
- if (is_untitled())
- save_file_as();
- else
- save_file(get_filename(), get_encoding());
- }
- // emergency_save() - called by the SIGHUP handler to save the buffer.
- void Editor::emergency_save()
- {
- if (wedit.is_modified()) {
- u8string filename;
- filename.cformat("%s.save", is_untitled() ? PACKAGE : get_filename());
- save_file(filename.c_str(), get_encoding());
- }
- }
-
- INTERACTIVE void Editor::load_file()
- {
- if (wedit.is_modified())
- if (!dialog.ask_yes_or_no(_("Buffer was modified; discard changes?")))
- return;
- u8string qry_filename, qry_encoding;
- if (query_filename(_("Open file:"), qry_filename, qry_encoding, LOAD_HISTORY))
- load_file(qry_filename.c_str(),
- qry_encoding.empty() ? NULL : qry_encoding.c_str());
- }
- INTERACTIVE void Editor::insert_file()
- {
- u8string qry_filename, qry_encoding;
- if (query_filename(_("Insert file:"), qry_filename, qry_encoding, INSERT_HISTORY))
- insert_file(qry_filename.c_str(),
- qry_encoding.empty() ? NULL : qry_encoding.c_str());
- }
- INTERACTIVE void Editor::change_directory()
- {
- u8string qry_dirname, dummy;
- if (query_filename(_("Change directory to:"), qry_dirname, dummy,
- CHDIR_HISTORY, InputLine::cmpltDirectories)) {
- if (chdir(qry_dirname.c_str()) == -1) {
- set_last_error(errno);
- show_file_io_error(_("Can't chdir to %s: %s"), qry_dirname.c_str());
- }
- }
- }
- void Editor::set_theme(const char *theme)
- {
- ThemeError theme_error;
- bool rslt;
- if (!theme)
- rslt = load_default_theme(theme_error);
- else
- rslt = load_theme(theme, theme_error);
- if (!rslt)
- show_file_io_error(_("Error: %s"), theme_error.format().c_str());
- refresh();
- }
- void Editor::set_default_theme()
- {
- set_theme(NULL);
- }
- const char *Editor::get_theme_name()
- {
- return ::get_theme_name();
- }
- void Editor::menu_set_encoding(bool default_encoding)
- {
- unistring answer = dialog.query(
- _("Enter encoding name (do 'iconv -l' at the shell for a list):"));
- if (!answer.empty()) {
- if (default_encoding)
- set_default_encoding(u8string(answer).c_str());
- else
- set_encoding(u8string(answer).c_str());
- }
- }
- // extract_encoding() - takes the filename that the user has entered,
- // of the form "/path/to/filename -encoding", and returns the "encoding"
- // part. it also removes it from the filename.
- static u8string extract_encoding(u8string &filename)
- {
- u8string encoding;
- int pos = filename.rindex(' ');
- if (pos != -1 && (filename[pos + 1] == '-' || filename[pos + 1] == '+')) {
- encoding.assign(filename, pos + 2, filename.size() - pos - 2);
- // we've found it. now remove the encoding part from the filename itself
- while (pos >= 0 && filename[pos] == ' ')
- --pos;
- filename.erase(filename.begin() + pos + 1, filename.end());
- }
- return encoding;
- }
- // query_filename() - prompt the user for a filename.
- bool Editor::query_filename(const char *label, u8string &qry_filename,
- u8string &qry_encoding, int history_set,
- InputLine::CompleteType complete)
- {
- unistring uni_filename;
- uni_filename.init_from_filename(qry_filename.c_str());
- // we feed dialog.query() not qry_filename but uni_filename.
- // this extra conversion is needed because qry_filename may not be
- // a valid UTF-8 string (this is possible since using UTF-8 for
- // filenames is only a convention).
- unistring answer = dialog.query(label, uni_filename, history_set, complete);
- qry_filename.init_from_unichars(answer);
- // extract encoding, but only if it's not a pipe.
- if (!qry_filename.empty() && qry_filename[0] != '|' && qry_filename[0] != '!')
- qry_encoding = extract_encoding(qry_filename);
- else
- qry_encoding = "";
- if (qry_filename == "~")
- qry_filename = "~/";
- expand_tilde(qry_filename);
- return !qry_filename.empty();
- }
- INTERACTIVE void Editor::change_tab_width()
- {
- int num = dialog.get_number(_("Enter tab width:"), wedit.get_tab_width());
- if (num <= 80) // arbitrary sanity check
- set_tab_width(num);
- }
- INTERACTIVE void Editor::change_justification_column()
- {
- int num = dialog.get_number(_("Enter justification column:"),
- wedit.get_justification_column());
- set_justification_column(num);
- }
- INTERACTIVE void Editor::change_scroll_step()
- {
- int num = dialog.get_number(_("Enter scroll step:"), wedit.get_scroll_step());
- wedit.set_scroll_step(num);
- }
- INTERACTIVE void Editor::insert_unicode_char()
- {
- unistring answer = dialog.query(
- _("Enter unicode hex value (or end in '.' for base 10):"));
- if (answer.empty())
- return;
- u8string numstr;
- numstr.init_from_unichars(answer);
- errno = 0;
- char *endp;
- unichar ch = strtol(numstr.c_str(), &endp,
- strchr(numstr.c_str(), '.') ? 10 : 16);
- if (!errno && (*endp == '.' || *endp == '\0'))
- wedit.insert_char(ch);
- }
- void Editor::log2vis(const char *options)
- {
- wedit.log2vis(options);
- }
- INTERACTIVE void Editor::toggle_arabic_shaping()
- {
- terminal::do_arabic_shaping = !terminal::do_arabic_shaping;
- wedit.reformat();
- if (terminal::is_interactive())
- refresh();
- }
- INTERACTIVE void Editor::toggle_graphical_boxes()
- {
- terminal::graphical_boxes = !terminal::graphical_boxes;
- if (terminal::is_interactive())
- refresh();
- }
- void Editor::detect_syntax()
- {
- #define NMHAS(EXT) (strstr(get_filename(), EXT))
- if (NMHAS(".htm") || NMHAS(".HTM"))
- wedit.set_syn_hlt(EditBox::synhltHTML);
- else if (NMHAS(".letter") || NMHAS(".article") || NMHAS(".followup") ||
- NMHAS(".eml") || NMHAS("SLRN") || NMHAS("pico") || NMHAS("mutt"))
- // the above strings were taken from Vim's cfg.
- wedit.set_syn_hlt(EditBox::synhltEmail);
- else
- wedit.detect_syntax();
- #undef NMHAS
- }
- void Editor::set_syntax_auto_detection(bool v)
- {
- syntax_auto_detection = v;
- if (v)
- detect_syntax();
- }
- INTERACTIVE void Editor::toggle_syntax_auto_detection()
- {
- set_syntax_auto_detection(!get_syntax_auto_detection());
- }
- INTERACTIVE void Editor::go_to_line()
- {
- bool canceled;
- int num = dialog.get_number(_("Go to line #:"), 0, &canceled);
- if (!canceled)
- go_to_line(num);
- }
- void Editor::search_forward(const unistring &search)
- {
- if (!wedit.search_forward(search))
- show_kbd_error(_("Not found"));
- last_searched_string = search;
- }
- INTERACTIVE void Editor::search_forward()
- {
- bool alt_kbd = wedit.get_alt_kbd();
- unistring search = dialog.query(_("Search forward:"),
- last_searched_string, SEARCH_HISTORY,
- InputLine::cmpltOff, &alt_kbd);
- wedit.set_alt_kbd(alt_kbd);
- if (!search.empty())
- search_forward(search);
- }
-
- INTERACTIVE void Editor::search_forward_next()
- {
- if (!last_searched_string.empty())
- search_forward(last_searched_string);
- else
- search_forward();
- }
- u8string Editor::get_external_editor()
- {
- if (!external_editor.empty())
- return external_editor;
- // User hasn't specified an editor, so we figure one out ourselves:
- // First try $EDITOR, them gvim, then vim, then ...
- if (getenv("EDITOR") && *getenv("EDITOR"))
- return getenv("EDITOR");
- if (terminal::under_x11() && has_prog("gvim"))
- return "gvim -f"; // `-f' = not to fork and detach
- if (has_prog("vim"))
- return "vim";
- if (has_prog("emacs"))
- return "emacs";
- if (has_prog("pico"))
- return "pico";
- return "vi";
- }
- void Editor::set_external_editor(const char *cmd)
- {
- external_editor = cmd;
- }
- INTERACTIVE void Editor::external_edit_prompt()
- {
- external_edit(true);
- }
- INTERACTIVE void Editor::external_edit_no_prompt()
- {
- external_edit(false);
- }
- // external_edit() launches an external editor to edit the file
- // and then reload it.
- void Editor::external_edit(bool prompt)
- {
- // Step 1: we may first need to save the file.
- if (is_untitled()) {
- if (dialog.ask_yes_or_no(_("First I must save this buffer as a file; is it OK with you?")))
- save_file();
- } else if (wedit.is_modified()) {
- if (dialog.ask_yes_or_no(_("Buffer was modified and I must save it first; is it OK with you?")))
- save_file();
- }
- if (is_untitled() || wedit.is_modified())
- return;
- // Step 2: get the editor command
- cstring command = get_external_editor();
- if (prompt) {
- unistring inpt = dialog.query(_("External editor:"),
- command.c_str(),
- EXTERNALEDITOR_HISTORY,
- InputLine::cmpltAll);
- command = u8string(inpt).trim();
- if (command.empty()) // user aborted
- return;
- set_external_editor(command.c_str());
- }
- // Step 3: write the cursor location into a temporary file,
- // pointed to by $GERESH_CURSOR_FILE
-
- cstring cursor_file;
- //cursor_file = tmpnam(NULL); // gives linker warning!
- cursor_file.cformat("%s/geresh-%ld-cursor.tmp",
- getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp",
- (long)getpid());
- Point cursor;
- wedit.get_cursor_position(cursor);
- FILE *cursor_fh;
- if ((cursor_fh = fopen(cursor_file.c_str(), "w")) != NULL) {
- fprintf(cursor_fh, "%d %d\n%s\n",
- cursor.para + 1, cursor.pos + 1, get_filename());
- fclose(cursor_fh);
- cstring nameval;
- nameval.cformat("GERESH_CURSOR_FILE=%s", cursor_file.c_str());
- putenv(strdup(nameval.c_str()));
- }
- // Step 4: adjust the editor command so the line number is passed.
- if (command.index("vim") != -1) {
- // With vim we not only pass the column number in addition
- // to the line number, but we also pass the necessary
- // command to write the cursor position back into
- // $GERESH_CURSOR_FILE.
- cstring basefilename = get_filename();
- if (basefilename.index('/') != -1)
- basefilename = basefilename.substr(basefilename.rindex('/')+1);
- cstring col_command;
- if (cursor.pos != 0)
- col_command.cformat("-c \"normal %dl\"", cursor.pos);
- command.cformat("%s -c %d -c \"normal 0\" %s "
- "-c \"au BufUnload %s call system('echo '.line('.').' '.col('.').' >%s')\"",
- command.c_str(),
- cursor.para + 1,
- col_command.c_str(),
- basefilename.c_str(),
- cursor_file.c_str());
- }
- else if (command.index("vi") != -1 ||
- command.index("pico") != -1 ||
- command.index("nano") != -1 ||
- command.index("emacs") != -1 ||
- command.index("mined") != -1)
- {
- // The above editors are known to accept a "+line" parameter.
- command.cformat("%s +%d", command.c_str(), cursor.para + 1);
- }
- command += " \"";
- command += get_filename();
- command += "\"";
- // Step 5: execute the editor.
- cstring msg;
- msg.cformat("Executing: %s", command.c_str());
- dialog.show_message(msg.c_str());
- update_terminal();
-
- endwin();
- int status = system(command.c_str());
- doupdate();
-
- // Step 6: reload the file.
- // I can't just pass 'get_filename()' to load_file(), because
- // get_filename() returns a pointer to a string which load_file()
- // modifies (via set_filename()) ...
- load_file(u8string(get_filename()).c_str(),
- u8string(get_encoding()).c_str());
-
- // Step 7: read the cursor position from $GERESH_CURSOR_FILE.
- if ((cursor_fh = fopen(cursor_file.c_str(), "r")) != NULL) {
- int line_no, col_no;
- fscanf(cursor_fh, "%d %d", &line_no, &col_no);
- cursor.para = line_no - 1;
- cursor.pos = col_no - 1;
- wedit.set_cursor_position(cursor);
- fclose(cursor_fh);
- } else {
- wedit.set_cursor_position(cursor);
- }
- unlink(cursor_file.c_str());
- if (WIFEXITED(status) && WEXITSTATUS(status) == 127) {
- msg.cformat(_("Error: command \"%s\" could not be found"),
- get_external_editor().c_str());
- dialog.show_message(msg.c_str());
- }
- refresh_and_center();
- }
- // layout_windows() - is called every time the window changes its size.
- INTERACTIVE void Editor::layout_windows()
- {
- int cols, rows;
- getmaxyx(stdscr, rows, cols);
- int speller_height = 0;
- if (spellerwnd) {
- // determine the height of the speller window
- // based on the screen size.
- if (rows <= 10)
- speller_height = 2;
- else if (rows <= 16)
- speller_height = 4;
- else if (rows <= 20)
- speller_height = 6;
- else if (rows <= 26)
- speller_height = 8;
- else if (rows <= 36)
- speller_height = 11;
- else
- speller_height = 13;
-
- spellerwnd->resize(speller_height, cols, rows - 2 - speller_height, 0);
- }
- if (menubar)
- menubar->resize(1, cols, 0, 0);
- #define MENUHEIGHT (menubar ? 1 : 0)
- if (scrollbar)
- scrollbar->resize(rows - 2 - MENUHEIGHT - speller_height, 1,
- MENUHEIGHT, (scrollbar_pos == scrlbrLeft) ? 0 : cols - 1);
- #define SCROLLBARWIDTH (scrollbar ? 1 : 0)
- wedit.resize(rows - 2 - MENUHEIGHT - speller_height,
- cols - SCROLLBARWIDTH, MENUHEIGHT,
- (scrollbar_pos == scrlbrLeft) ? 1 : 0);
- status.resize(1, cols, rows - 2, 0);
- dialog.resize(1, cols, rows - 1, 0);
- update_terminal();
- }
- // update_terminal() - updates the screen.
- void Editor::update_terminal(bool soft)
- {
- // for every widget that's dirty, call its update() method to update
- // stdscr. If any was dirty, call doupdate() to update the physical
- // screen.
- bool need_dorefresh = false;
- if (scrollbar && scrollbar->is_dirty()) {
- scrollbar->update();
- need_dorefresh = true;
- }
- if (menubar && menubar->is_dirty()) {
- menubar->update();
- need_dorefresh = true;
- }
-
- if (status.is_dirty()) {
- status.update();
- need_dorefresh = true;
- }
- if (dialog.is_dirty()) {
- dialog.update();
- need_dorefresh = true;
- }
-
- if (wedit.is_dirty()) {
- wedit.update();
- need_dorefresh = true;
- } else if (need_dorefresh) {
- // make sure wedit gets the cursor even if some
- // of the other widgets get drawn.
- wedit.update_cursor();
- }
- if (spellerwnd) {
- if (spellerwnd->is_dirty()) {
- spellerwnd->update();
- need_dorefresh = true;
- } else if (need_dorefresh) {
- // spellerwnd always gets the cursor.
- spellerwnd->update_cursor();
- }
- }
- if (!soft && need_dorefresh)
- doupdate();
- }
- // Editor::exec() - the main event loop. it reads events and either handles
- // them or send them on to the editbox.
- void Editor::exec()
- {
- finished = false;
- while (!finished) {
- Event evt;
- update_terminal();
- get_next_event(evt, wedit.wnd);
- dialog.clear_transient_message();
- if (!evt.is_literal())
- if (handle_event(evt))
- continue;
- wedit.handle_event(evt);
- if (scrollbar)
- wedit.sync_scrollbar(scrollbar);
- }
- }
- // load_unload_speller() - interactively loads and unloads a speller
- // process. When the user interactively loads a speller, he is asked to
- // specify the speller-command and the speller-encoding.
- INTERACTIVE void Editor::load_unload_speller()
- {
- if (speller.is_loaded()) {
- speller.unload();
- } else {
- // We're about to load the speller. query the user for cmd / encoding.
- unistring cmd = dialog.query(_("Enter speller command:"),
- get_speller_cmd(), SPELER_CMD_HISTORY, InputLine::cmpltAll);
- if (cmd.empty())
- return;
- if (cmd.index(u8string("-a")) == -1) {
- if (!dialog.ask_yes_or_no(
- _("There's no `-a' option in the command, proceed anyway?")))
- return;
- }
- unistring encoding = dialog.query(_("Enter speller encoding:"),
- get_speller_encoding(), SPELER_ENCODING_HISTORY);
- if (encoding.empty())
- return;
- set_speller_cmd(u8string(cmd).c_str());
- set_speller_encoding(u8string(encoding).c_str());
- speller.load(get_speller_cmd(), get_speller_encoding());
- }
- status.invalidate_view();
- }
- // adjust_speller_cmd() - if the user hasn't specified a speller command,
- // figure it out ourselves.
- // Prefer: multispell, then ispell, and finally aspell.
- // Use the ISO-8859-8 encoding for Hebrew spell-checkers.
- void Editor::adjust_speller_cmd()
- {
- bool no_cmd = (*get_speller_cmd() == '\0');
- bool no_encoding = (*get_speller_encoding() == '\0');
- if (no_cmd) {
- bool has_multispell = has_prog("multispell");
- bool has_ispell = has_prog("ispell");
- bool has_aspell = !has_ispell && has_prog("aspell"); // optimization: search aspell
- // only if no ispell found.
- if (has_multispell) {
- u8string cmd;
- if (has_ispell || has_aspell) {
- cmd = "LC_ALL=C multispell -a -n";
- if (!has_ispell && has_aspell)
- cmd += " --ispell-cmd=aspell";
- } else {
- cmd = "LC_ALL=C hspell -a -n";
- }
- set_speller_cmd(cmd.c_str());
- set_speller_encoding("ISO-8859-8");
- } else {
- if (!has_ispell && has_aspell)
- set_speller_cmd("aspell -a");
- else
- set_speller_cmd("ispell -a");
- if (no_encoding)
- set_speller_encoding("ISO-8859-1");
- }
- } else if (no_encoding) {
- if (strstr(get_speller_cmd(), "hspell") || strstr(get_speller_cmd(), "multispell"))
- set_speller_encoding("ISO-8859-8");
- else
- set_speller_encoding("ISO-8859-1");
- }
- }
- INTERACTIVE void Editor::spell_check_all()
- {
- spell_check(Speller::splRngAll);
- }
- INTERACTIVE void Editor::spell_check_forward()
- {
- spell_check(Speller::splRngForward);
- }
- INTERACTIVE void Editor::spell_check_word()
- {
- spell_check(Speller::splRngWord);
- }
- void Editor::spell_check(Speller::splRng range)
- {
- if (!spellerwnd) {
- spellerwnd = new SpellerWnd(*this);
- layout_windows();
- }
- // if the speller is not yet loaded, load it with the default
- // parameters.
- if (!speller.is_loaded()) {
- speller.load(get_speller_cmd(), get_speller_encoding());
- status.invalidate_view();
- update_terminal(); // immediately update the status line
- }
- if (speller.is_loaded())
- speller.spell_check(range, wedit, *spellerwnd);
- // The speller window exists during the spell check only. we
- // delete it afterwards.
- delete spellerwnd;
- spellerwnd = NULL;
- layout_windows();
- }
- // show_hint() is used to print the popdown menu hints. Screen is updated
- // only after doupdate()
- void Editor::show_hint(const char *msg)
- {
- dialog.show_message(msg);
- dialog.update();
- }
- // show_kbd_error() - prints a transient error message and rings the bell.
- // it is usually used for "keyboard" errors and the bell is supposed to
- // catch the users attention.
- void Editor::show_kbd_error(const char *msg)
- {
- // we don't use show_error_message() because we want the
- // message to disappear at the next event.
- dialog.show_message(msg);
- Widget::signal_error();
- }
- void Editor::on_read_only_error(unichar ch)
- {
- unistring quit_chars;
- // when the buffer is in read-only mode, we let the user exit
- // by pressing 'q'.
- quit_chars.init_from_utf8(_("qQ"));
- if (quit_chars.has_char(ch))
- quit();
- else
- show_kbd_error(_("Buffer is read-only"));
- }
- void Editor::on_no_selection_error()
- {
- show_kbd_error(_("No text is selected"));
- }
- void Editor::on_no_alt_kbd_error()
- {
- show_kbd_error(_("No alternate keyboard (Hebrew) was loaded"));
- }
- void Editor::on_no_translation_table_error()
- {
- show_kbd_error(_("No translation table was loaded"));
- }
- void Editor::on_cant_display_nsm_error()
- {
- show_kbd_error(_("Terminal can't display non-spacing marks (like Hebrew points)"));
- }
|