3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
21 #include "buffer_funcs.h"
22 #include "bufferlist.h"
23 #include "converter.h"
30 #include "lastfiles.h"
33 #include "lyxtextclasslist.h"
34 #include "lyxserver.h"
35 #include "MenuBackend.h"
36 #include "ToolbarBackend.h"
38 #include "frontends/Alert.h"
39 #include "frontends/lyx_gui.h"
42 #include "support/FileInfo.h"
43 #include "support/filetools.h"
44 #include "support/lyxlib.h"
45 #include "support/os.h"
46 #include "support/path.h"
47 #include "support/path_defines.h"
49 #include <boost/bind.hpp>
53 using namespace lyx::support;
58 #ifndef CXX_GLOBAL_CSTD
64 extern void QuitLyX();
66 extern LyXServer * lyxserver;
70 boost::scoped_ptr<LastFiles> lastfiles;
72 // This is the global bufferlist object
73 BufferList bufferlist;
75 // convenient to have it here.
76 boost::scoped_ptr<kb_keymap> toplevel_keymap;
80 void showFileError(string const & error)
82 Alert::warning(_("Could not read configuration file"),
83 bformat(_("Error while reading the configuration file\n%1$s.\n"
84 "Please check your installation."), error));
90 LyX::LyX(int & argc, char * argv[])
92 // Here we need to parse the command line. At least
93 // we need to parse for "-dbg" and "-help"
94 bool const want_gui = easyParse(argc, argv);
96 // set the DisplayTranslator only once; should that be done here??
97 // if this should not be in this file, please also remove
98 // #include "graphics/GraphicsTypes.h" at the top -- Rob Lahaye.
99 lyx::graphics::setDisplayTranslator();
102 lyx_gui::parse_init(argc, argv);
104 // check for any spurious extra arguments
105 // other than documents
106 for (int argi = 1; argi < argc ; ++argi) {
107 if (argv[argi][0] == '-') {
108 lyxerr << bformat(_("Wrong command line option `%1$s'. Exiting."),
114 // Initialization of LyX (reads lyxrc and more)
115 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
117 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
120 lyx_gui::parse_lyxrc();
122 vector<string> files;
124 for (int argi = argc - 1; argi >= 1; --argi)
125 files.push_back(argv[argi]);
128 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
130 // Execute batch commands if available
131 if (!batch_command.empty()) {
133 lyxerr[Debug::INIT] << "About to handle -x '"
134 << batch_command << '\'' << endl;
136 Buffer * last_loaded = 0;
138 vector<string>::const_iterator it = files.begin();
139 vector<string>::const_iterator end = files.end();
141 for (; it != end; ++it) {
142 // get absolute path of file and add ".lyx" to
143 // the filename if necessary
144 string s = FileSearch(string(), *it, "lyx");
146 last_loaded = newFile(*it, string(), true);
148 Buffer * buf = bufferlist.newBuffer(s, false);
149 buf->error.connect(boost::bind(&LyX::printError, this, _1));
150 if (loadLyXFile(buf, s))
153 bufferlist.release(buf);
157 // try to dispatch to last loaded buffer first
159 bool success = false;
160 if (last_loaded->dispatch(batch_command, &success)) {
165 files.clear(); // the files are already loaded
168 lyx_gui::start(batch_command, files);
174 static void error_handler(int err_sig)
178 lyxerr << "\nlyx: SIGHUP signal caught" << endl;
184 lyxerr << "\nlyx: SIGFPE signal caught" << endl;
187 lyxerr << "\nlyx: SIGSEGV signal caught" << endl;
189 "Sorry, you have found a bug in LyX. "
190 "Please read the bug-reporting instructions "
191 "in Help->Introduction and send us a bug report, "
192 "if necessary. Thanks !" << endl;
199 // Deinstall the signal handlers
200 signal(SIGHUP, SIG_DFL);
201 signal(SIGINT, SIG_DFL);
202 signal(SIGFPE, SIG_DFL);
203 signal(SIGSEGV, SIG_DFL);
204 signal(SIGTERM, SIG_DFL);
206 LyX::emergencyCleanup();
208 lyxerr << "Bye." << endl;
209 if (err_sig!= SIGHUP &&
210 (!GetEnv("LYXDEBUG").empty() || err_sig == SIGSEGV))
211 lyx::support::abort();
218 void LyX::printError(ErrorItem const & ei)
220 std::cerr << _("LyX: ") << ei.error
221 << ':' << ei.description << std::endl;
226 void LyX::init(bool gui)
228 signal(SIGHUP, error_handler);
229 signal(SIGFPE, error_handler);
230 signal(SIGSEGV, error_handler);
231 signal(SIGINT, error_handler);
232 signal(SIGTERM, error_handler);
234 bool const explicit_userdir = setLyxPaths();
236 // Check that user LyX directory is ok. We don't do that if
237 // running in batch mode.
239 queryUserLyXDir(explicit_userdir);
244 // Disable gui when easyparse says so
245 lyx_gui::use_gui = gui;
247 if (lyxrc.template_path.empty()) {
248 lyxrc.template_path = AddPath(system_lyxdir(), "templates");
251 if (lyxrc.lastfiles.empty()) {
252 lyxrc.lastfiles = AddName(user_lyxdir(), "lastfiles");
255 if (lyxrc.roman_font_name.empty())
256 lyxrc.roman_font_name = lyx_gui::roman_font_name();
257 if (lyxrc.sans_font_name.empty())
258 lyxrc.sans_font_name = lyx_gui::sans_font_name();
259 if (lyxrc.typewriter_font_name.empty())
260 lyxrc.typewriter_font_name = lyx_gui::typewriter_font_name();
263 // Read configuration files
266 readRcFile("lyxrc.defaults");
267 system_lyxrc = lyxrc;
268 system_formats = formats;
269 system_converters = converters;
270 system_lcolor = lcolor;
272 string prefsfile = "preferences";
273 // back compatibility to lyxs < 1.1.6
274 if (LibFileSearch(string(), prefsfile).empty())
276 if (!LibFileSearch(string(), prefsfile).empty())
277 readRcFile(prefsfile);
279 readEncodingsFile("encodings");
280 readLanguagesFile("languages");
283 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
288 toplevel_keymap.reset(new kb_keymap);
289 defaultKeyBindings(toplevel_keymap.get());
290 toplevel_keymap->read(lyxrc.bind_file);
293 readUIFile(lyxrc.ui_file);
296 if (lyxerr.debugging(Debug::LYXRC))
299 os::setTmpDir(CreateLyXTmpDir(lyxrc.tempdir_path));
300 if (lyxerr.debugging(Debug::INIT)) {
301 lyxerr << "LyX tmp dir: `" << os::getTmpDir() << '\'' << endl;
304 lyxerr[Debug::INIT] << "Reading lastfiles `"
305 << lyxrc.lastfiles << "'..." << endl;
306 lastfiles.reset(new LastFiles(lyxrc.lastfiles,
307 lyxrc.check_lastfiles,
308 lyxrc.num_lastfiles));
312 void LyX::defaultKeyBindings(kb_keymap * kbmap)
314 kbmap->bind("Right", LFUN_RIGHT);
315 kbmap->bind("Left", LFUN_LEFT);
316 kbmap->bind("Up", LFUN_UP);
317 kbmap->bind("Down", LFUN_DOWN);
319 kbmap->bind("Tab", LFUN_CELL_FORWARD);
320 kbmap->bind("ISO_Left_Tab", LFUN_CELL_FORWARD); // jbl 2001-23-02
322 kbmap->bind("Home", LFUN_HOME);
323 kbmap->bind("End", LFUN_END);
324 kbmap->bind("Prior", LFUN_PRIOR);
325 kbmap->bind("Next", LFUN_NEXT);
327 kbmap->bind("Return", LFUN_BREAKPARAGRAPH);
328 //kbmap->bind("~C-~S-~M-nobreakspace", LFUN_PROTECTEDSPACE);
330 kbmap->bind("Delete", LFUN_DELETE);
331 kbmap->bind("BackSpace", LFUN_BACKSPACE);
333 // sub- and superscript -MV
334 kbmap->bind("~S-underscore", LFUN_SUBSCRIPT);
335 kbmap->bind("~S-asciicircum", LFUN_SUPERSCRIPT);
337 // kbmap->bindings to enable the use of the numeric keypad
339 //kbmap->bind("KP_0", LFUN_SELFINSERT);
340 //kbmap->bind("KP_Decimal", LFUN_SELFINSERT);
341 kbmap->bind("KP_Enter", LFUN_BREAKPARAGRAPH);
342 //kbmap->bind("KP_1", LFUN_SELFINSERT);
343 //kbmap->bind("KP_2", LFUN_SELFINSERT);
344 //kbmap->bind("KP_3", LFUN_SELFINSERT);
345 //kbmap->bind("KP_4", LFUN_SELFINSERT);
346 //kbmap->bind("KP_5", LFUN_SELFINSERT);
347 //kbmap->bind("KP_6", LFUN_SELFINSERT);
348 //kbmap->bind("KP_Add", LFUN_SELFINSERT);
349 //kbmap->bind("KP_7", LFUN_SELFINSERT);
350 //kbmap->bind("KP_8", LFUN_SELFINSERT);
351 //kbmap->bind("KP_9", LFUN_SELFINSERT);
352 //kbmap->bind("KP_Divide", LFUN_SELFINSERT);
353 //kbmap->bind("KP_Multiply", LFUN_SELFINSERT);
354 //kbmap->bind("KP_Subtract", LFUN_SELFINSERT);
355 kbmap->bind("KP_Right", LFUN_RIGHT);
356 kbmap->bind("KP_Left", LFUN_LEFT);
357 kbmap->bind("KP_Up", LFUN_UP);
358 kbmap->bind("KP_Down", LFUN_DOWN);
359 kbmap->bind("KP_Home", LFUN_HOME);
360 kbmap->bind("KP_End", LFUN_END);
361 kbmap->bind("KP_Prior", LFUN_PRIOR);
362 kbmap->bind("KP_Next", LFUN_NEXT);
364 kbmap->bind("C-Tab", LFUN_CELL_SPLIT); // ale970515
365 kbmap->bind("S-Tab", LFUN_CELL_BACKWARD); // jug20000522
366 kbmap->bind("S-ISO_Left_Tab", LFUN_CELL_BACKWARD); // jbl 2001-23-02
370 void LyX::emergencyCleanup()
372 // what to do about tmpfiles is non-obvious. we would
373 // like to delete any we find, but our lyxdir might
374 // contain documents etc. which might be helpful on
377 bufferlist.emergencyWriteAll();
379 lyxserver->emergencyCleanup();
383 void LyX::deadKeyBindings(kb_keymap * kbmap)
385 // bindKeyings for transparent handling of deadkeys
386 // The keysyms are gotten from XFree86 X11R6
387 kbmap->bind("~C-~S-~M-dead_acute", LFUN_ACUTE);
388 kbmap->bind("~C-~S-~M-dead_breve", LFUN_BREVE);
389 kbmap->bind("~C-~S-~M-dead_caron", LFUN_CARON);
390 kbmap->bind("~C-~S-~M-dead_cedilla", LFUN_CEDILLA);
391 kbmap->bind("~C-~S-~M-dead_abovering", LFUN_CIRCLE);
392 kbmap->bind("~C-~S-~M-dead_circumflex", LFUN_CIRCUMFLEX);
393 kbmap->bind("~C-~S-~M-dead_abovedot", LFUN_DOT);
394 kbmap->bind("~C-~S-~M-dead_grave", LFUN_GRAVE);
395 kbmap->bind("~C-~S-~M-dead_doubleacute", LFUN_HUNG_UMLAUT);
396 kbmap->bind("~C-~S-~M-dead_macron", LFUN_MACRON);
397 // nothing with this name
398 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_SPECIAL_CARON);
399 kbmap->bind("~C-~S-~M-dead_tilde", LFUN_TILDE);
400 kbmap->bind("~C-~S-~M-dead_diaeresis", LFUN_UMLAUT);
401 // nothing with this name either...
402 //kbmap->bind("~C-~S-~M-dead_underbar", LFUN_UNDERBAR);
403 kbmap->bind("~C-~S-~M-dead_belowdot", LFUN_UNDERDOT);
404 kbmap->bind("~C-~S-~M-dead_tie", LFUN_TIE);
405 kbmap->bind("~C-~S-~M-dead_ogonek", LFUN_OGONEK);
409 void LyX::queryUserLyXDir(bool explicit_userdir)
411 string const configure_script = AddName(system_lyxdir(), "configure");
413 // Does user directory exist?
414 FileInfo fileInfo(user_lyxdir());
415 if (fileInfo.isOK() && fileInfo.isDir()) {
417 FileInfo script(configure_script);
418 FileInfo defaults(AddName(user_lyxdir(), "lyxrc.defaults"));
419 if (defaults.isOK() && script.isOK()
420 && defaults.getModificationTime() < script.getModificationTime()) {
421 lyxerr << _("LyX: reconfiguring user directory")
423 Path p(user_lyxdir());
424 ::system(configure_script.c_str());
425 lyxerr << "LyX: " << _("Done!") << endl;
430 first_start = !explicit_userdir;
432 lyxerr << bformat(_("LyX: Creating directory %1$s"
433 " and running configure..."), user_lyxdir()) << endl;
435 if (!createDirectory(user_lyxdir(), 0755)) {
436 // Failed, let's use $HOME instead.
437 user_lyxdir(GetEnvPath("HOME"));
438 lyxerr << bformat(_("Failed. Will use %1$s instead."),
439 user_lyxdir()) << endl;
443 // Run configure in user lyx directory
444 Path p(user_lyxdir());
445 ::system(configure_script.c_str());
446 lyxerr << "LyX: " << _("Done!") << endl;
450 void LyX::readRcFile(string const & name)
452 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
454 string const lyxrc_path = LibFileSearch(string(), name);
455 if (!lyxrc_path.empty()) {
457 lyxerr[Debug::INIT] << "Found " << name
458 << " in " << lyxrc_path << endl;
460 if (lyxrc.read(lyxrc_path) >= 0)
468 // Read the ui file `name'
469 void LyX::readUIFile(string const & name)
479 struct keyword_item uitags[ui_last - 1] = {
480 { "include", ui_include },
481 { "menuset", ui_menuset },
482 { "toolbar", ui_toolbar },
483 { "toolbars", ui_toolbars }
486 // Ensure that a file is read only once (prevents include loops)
487 static std::list<string> uifiles;
488 std::list<string>::const_iterator it = uifiles.begin();
489 std::list<string>::const_iterator end = uifiles.end();
490 it = std::find(it, end, name);
492 lyxerr[Debug::INIT] << "UI file '" << name
493 << "' has been read already. "
494 << "Is this an include loop?"
499 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
501 string const ui_path = LibFileSearch("ui", name, "ui");
503 if (ui_path.empty()) {
504 lyxerr[Debug::INIT] << "Could not find " << name << endl;
508 uifiles.push_back(name);
510 lyxerr[Debug::INIT] << "Found " << name
511 << " in " << ui_path << endl;
512 LyXLex lex(uitags, ui_last - 1);
513 lex.setFile(ui_path);
515 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
519 if (lyxerr.debugging(Debug::PARSER))
520 lex.printTable(lyxerr);
526 string const file = lex.getString();
531 menubackend.read(lex);
535 toolbarbackend.read(lex);
539 toolbarbackend.readToolbars(lex);
543 if (!rtrim(lex.getString()).empty())
544 lex.printError("LyX::ReadUIFile: "
545 "Unknown menu tag: `$$Token'");
552 // Read the languages file `name'
553 void LyX::readLanguagesFile(string const & name)
555 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
557 string const lang_path = LibFileSearch(string(), name);
558 if (lang_path.empty()) {
562 languages.read(lang_path);
566 // Read the encodings file `name'
567 void LyX::readEncodingsFile(string const & name)
569 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
571 string const enc_path = LibFileSearch(string(), name);
572 if (enc_path.empty()) {
576 encodings.read(enc_path);
585 /// return the the number of arguments consumed
586 typedef boost::function<int(string const &, string const &)> cmd_helper;
588 int parse_dbg(string const & arg, string const &)
591 lyxerr << _("List of supported debug flags:") << endl;
592 Debug::showTags(lyxerr);
595 lyxerr << bformat(_("Setting debug level to %1$s"), arg) << endl;
597 lyxerr.level(Debug::value(arg));
598 Debug::showLevel(lyxerr, lyxerr.level());
603 int parse_help(string const &, string const &)
606 _("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
607 "Command line switches (case sensitive):\n"
608 "\t-help summarize LyX usage\n"
609 "\t-userdir dir try to set user directory to dir\n"
610 "\t-sysdir dir try to set system directory to dir\n"
611 "\t-geometry WxH+X+Y set geometry of the main window\n"
612 "\t-dbg feature[,feature]...\n"
613 " select the features to debug.\n"
614 " Type `lyx -dbg' to see the list of features\n"
615 "\t-x [--execute] command\n"
616 " where command is a lyx command.\n"
617 "\t-e [--export] fmt\n"
618 " where fmt is the export format of choice.\n"
619 "\t-i [--import] fmt file.xxx\n"
620 " where fmt is the import format of choice\n"
621 " and file.xxx is the file to be imported.\n"
622 "\t-version summarize version and build info\n"
623 "Check the LyX man page for more details.") << endl;
628 int parse_version(string const &, string const &)
630 lyxerr << "LyX " << lyx_version
631 << " of " << lyx_release_date << endl;
632 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
634 lyxerr << lyx_version_info << endl;
639 int parse_sysdir(string const & arg, string const &)
642 lyxerr << _("Missing directory for -sysdir switch") << endl;
649 int parse_userdir(string const & arg, string const &)
652 lyxerr << _("Missing directory for -userdir switch") << endl;
659 int parse_execute(string const & arg, string const &)
662 lyxerr << _("Missing command string after --execute switch") << endl;
666 // Argh. Setting gui to false segfaults..
667 // FIXME: when ? how ?
672 int parse_export(string const & type, string const &)
675 lyxerr << _("Missing file type [eg latex, ps...] after "
676 "--export switch") << endl;
679 batch = "buffer-export " + type;
684 int parse_import(string const & type, string const & file)
687 lyxerr << _("Missing file type [eg latex, ps...] after "
688 "--import switch") << endl;
692 lyxerr << _("Missing filename for --import") << endl;
696 batch = "buffer-import " + type + ' ' + file;
703 bool LyX::easyParse(int & argc, char * argv[])
705 std::map<string, cmd_helper> cmdmap;
707 cmdmap["-dbg"] = parse_dbg;
708 cmdmap["-help"] = parse_help;
709 cmdmap["--help"] = parse_help;
710 cmdmap["-version"] = parse_version;
711 cmdmap["--version"] = parse_version;
712 cmdmap["-sysdir"] = parse_sysdir;
713 cmdmap["-userdir"] = parse_userdir;
714 cmdmap["-x"] = parse_execute;
715 cmdmap["--execute"] = parse_execute;
716 cmdmap["-e"] = parse_export;
717 cmdmap["--export"] = parse_export;
718 cmdmap["-i"] = parse_import;
719 cmdmap["--import"] = parse_import;
721 for (int i = 1; i < argc; ++i) {
722 std::map<string, cmd_helper>::const_iterator it
723 = cmdmap.find(argv[i]);
725 // don't complain if not found - may be parsed later
726 if (it == cmdmap.end())
729 string arg((i + 1 < argc) ? argv[i + 1] : "");
730 string arg2((i + 2 < argc) ? argv[i + 2] : "");
732 int const remove = 1 + it->second(arg, arg2);
734 // Now, remove used arguments by shifting
735 // the following ones remove places down.
737 for (int j = i; j < argc; ++j)
738 argv[j] = argv[j + remove];
742 batch_command = batch;