3 * Copyright 1995-2002 the LyX Team
4 * Read the file COPYING
6 * \author Lars Gullik Bjønnes
14 #include "support/filetools.h"
15 #include "support/lyxlib.h"
16 #include "support/FileInfo.h"
17 #include "support/path.h"
18 #include "support/path_defines.h"
23 #include "graphics/GraphicsTypes.h"
25 #include "bufferlist.h"
27 #include "buffer_funcs.h"
28 #include "lyxserver.h"
31 #include "ToolbarBackend.h"
32 #include "MenuBackend.h"
34 #include "lastfiles.h"
36 #include "converter.h"
38 #include "lyxtextclasslist.h"
40 #include "frontends/Alert.h"
41 #include "frontends/lyx_gui.h"
43 #include <boost/function.hpp>
44 #include <boost/bind.hpp>
45 #include <boost/signals/signal1.hpp>
51 using namespace lyx::support;
56 #ifndef CXX_GLOBAL_CSTD
62 extern void QuitLyX();
64 extern LyXServer * lyxserver;
68 boost::scoped_ptr<LastFiles> lastfiles;
70 // This is the global bufferlist object
71 BufferList bufferlist;
73 // convenient to have it here.
74 boost::scoped_ptr<kb_keymap> toplevel_keymap;
78 void showFileError(string const & error)
80 Alert::warning(_("Could not read configuration file"),
81 bformat(_("Error while reading the configuration file\n%1$s.\n"
82 "Please check your installation."), error));
88 LyX::LyX(int & argc, char * argv[])
90 // Here we need to parse the command line. At least
91 // we need to parse for "-dbg" and "-help"
92 bool const want_gui = easyParse(argc, argv);
94 // set the DisplayTranslator only once; should that be done here??
95 // if this should not be in this file, please also remove
96 // #include "graphics/GraphicsTypes.h" at the top -- Rob Lahaye.
97 lyx::graphics::setDisplayTranslator();
100 lyx_gui::parse_init(argc, argv);
102 // check for any spurious extra arguments
103 // other than documents
104 for (int argi = 1; argi < argc ; ++argi) {
105 if (argv[argi][0] == '-') {
106 lyxerr << bformat(_("Wrong command line option `%1$s'. Exiting."),
112 // Initialization of LyX (reads lyxrc and more)
113 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
115 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
118 lyx_gui::parse_lyxrc();
120 vector<string> files;
122 for (int argi = argc - 1; argi >= 1; --argi)
123 files.push_back(argv[argi]);
126 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
128 // Execute batch commands if available
129 if (!batch_command.empty()) {
131 lyxerr[Debug::INIT] << "About to handle -x '"
132 << batch_command << '\'' << endl;
134 Buffer * last_loaded = 0;
136 vector<string>::const_iterator it = files.begin();
137 vector<string>::const_iterator end = files.end();
139 for (; it != end; ++it) {
140 // get absolute path of file and add ".lyx" to
141 // the filename if necessary
142 string s = FileSearch(string(), *it, "lyx");
144 last_loaded = newFile(*it, string(), true);
146 Buffer * buf = bufferlist.newBuffer(s, false);
147 buf->error.connect(boost::bind(&LyX::printError, this, _1));
148 if (loadLyXFile(buf, s))
151 bufferlist.release(buf);
155 // try to dispatch to last loaded buffer first
157 bool success = false;
158 if (last_loaded->dispatch(batch_command, &success)) {
163 files.clear(); // the files are already loaded
166 lyx_gui::start(batch_command, files);
172 static void error_handler(int err_sig)
176 lyxerr << "\nlyx: SIGHUP signal caught" << endl;
182 lyxerr << "\nlyx: SIGFPE signal caught" << endl;
185 lyxerr << "\nlyx: SIGSEGV signal caught" << endl;
187 "Sorry, you have found a bug in LyX. "
188 "Please read the bug-reporting instructions "
189 "in Help->Introduction and send us a bug report, "
190 "if necessary. Thanks !" << endl;
197 // Deinstall the signal handlers
198 signal(SIGHUP, SIG_DFL);
199 signal(SIGINT, SIG_DFL);
200 signal(SIGFPE, SIG_DFL);
201 signal(SIGSEGV, SIG_DFL);
202 signal(SIGTERM, SIG_DFL);
204 LyX::emergencyCleanup();
206 lyxerr << "Bye." << endl;
207 if (err_sig!= SIGHUP &&
208 (!GetEnv("LYXDEBUG").empty() || err_sig == SIGSEGV))
209 lyx::support::abort();
216 void LyX::printError(ErrorItem const & ei)
218 std::cerr << _("LyX: ") << ei.error
219 << ':' << ei.description << std::endl;
224 void LyX::init(bool gui)
226 signal(SIGHUP, error_handler);
227 signal(SIGFPE, error_handler);
228 signal(SIGSEGV, error_handler);
229 signal(SIGINT, error_handler);
230 signal(SIGTERM, error_handler);
232 bool const explicit_userdir = setLyxPaths();
234 // Check that user LyX directory is ok. We don't do that if
235 // running in batch mode.
237 queryUserLyXDir(explicit_userdir);
242 // Disable gui when easyparse says so
243 lyx_gui::use_gui = gui;
245 if (lyxrc.template_path.empty()) {
246 lyxrc.template_path = AddPath(system_lyxdir(), "templates");
249 if (lyxrc.lastfiles.empty()) {
250 lyxrc.lastfiles = AddName(user_lyxdir(), "lastfiles");
253 if (lyxrc.roman_font_name.empty())
254 lyxrc.roman_font_name = lyx_gui::roman_font_name();
255 if (lyxrc.sans_font_name.empty())
256 lyxrc.sans_font_name = lyx_gui::sans_font_name();
257 if (lyxrc.typewriter_font_name.empty())
258 lyxrc.typewriter_font_name = lyx_gui::typewriter_font_name();
261 // Read configuration files
264 readRcFile("lyxrc.defaults");
265 system_lyxrc = lyxrc;
266 system_formats = formats;
267 system_converters = converters;
268 system_lcolor = lcolor;
270 string prefsfile = "preferences";
271 // back compatibility to lyxs < 1.1.6
272 if (LibFileSearch(string(), prefsfile).empty())
274 if (!LibFileSearch(string(), prefsfile).empty())
275 readRcFile(prefsfile);
277 readEncodingsFile("encodings");
278 readLanguagesFile("languages");
281 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
286 toplevel_keymap.reset(new kb_keymap);
287 defaultKeyBindings(toplevel_keymap.get());
288 toplevel_keymap->read(lyxrc.bind_file);
291 readUIFile(lyxrc.ui_file);
294 if (lyxerr.debugging(Debug::LYXRC))
297 os::setTmpDir(CreateLyXTmpDir(lyxrc.tempdir_path));
298 if (lyxerr.debugging(Debug::INIT)) {
299 lyxerr << "LyX tmp dir: `" << os::getTmpDir() << '\'' << endl;
302 lyxerr[Debug::INIT] << "Reading lastfiles `"
303 << lyxrc.lastfiles << "'..." << endl;
304 lastfiles.reset(new LastFiles(lyxrc.lastfiles,
305 lyxrc.check_lastfiles,
306 lyxrc.num_lastfiles));
310 void LyX::defaultKeyBindings(kb_keymap * kbmap)
312 kbmap->bind("Right", LFUN_RIGHT);
313 kbmap->bind("Left", LFUN_LEFT);
314 kbmap->bind("Up", LFUN_UP);
315 kbmap->bind("Down", LFUN_DOWN);
317 kbmap->bind("Tab", LFUN_CELL_FORWARD);
318 kbmap->bind("ISO_Left_Tab", LFUN_CELL_FORWARD); // jbl 2001-23-02
320 kbmap->bind("Home", LFUN_HOME);
321 kbmap->bind("End", LFUN_END);
322 kbmap->bind("Prior", LFUN_PRIOR);
323 kbmap->bind("Next", LFUN_NEXT);
325 kbmap->bind("Return", LFUN_BREAKPARAGRAPH);
326 //kbmap->bind("~C-~S-~M-nobreakspace", LFUN_PROTECTEDSPACE);
328 kbmap->bind("Delete", LFUN_DELETE);
329 kbmap->bind("BackSpace", LFUN_BACKSPACE);
331 // sub- and superscript -MV
332 kbmap->bind("~S-underscore", LFUN_SUBSCRIPT);
333 kbmap->bind("~S-asciicircum", LFUN_SUPERSCRIPT);
335 // kbmap->bindings to enable the use of the numeric keypad
337 //kbmap->bind("KP_0", LFUN_SELFINSERT);
338 //kbmap->bind("KP_Decimal", LFUN_SELFINSERT);
339 kbmap->bind("KP_Enter", LFUN_BREAKPARAGRAPH);
340 //kbmap->bind("KP_1", LFUN_SELFINSERT);
341 //kbmap->bind("KP_2", LFUN_SELFINSERT);
342 //kbmap->bind("KP_3", LFUN_SELFINSERT);
343 //kbmap->bind("KP_4", LFUN_SELFINSERT);
344 //kbmap->bind("KP_5", LFUN_SELFINSERT);
345 //kbmap->bind("KP_6", LFUN_SELFINSERT);
346 //kbmap->bind("KP_Add", LFUN_SELFINSERT);
347 //kbmap->bind("KP_7", LFUN_SELFINSERT);
348 //kbmap->bind("KP_8", LFUN_SELFINSERT);
349 //kbmap->bind("KP_9", LFUN_SELFINSERT);
350 //kbmap->bind("KP_Divide", LFUN_SELFINSERT);
351 //kbmap->bind("KP_Multiply", LFUN_SELFINSERT);
352 //kbmap->bind("KP_Subtract", LFUN_SELFINSERT);
353 kbmap->bind("KP_Right", LFUN_RIGHT);
354 kbmap->bind("KP_Left", LFUN_LEFT);
355 kbmap->bind("KP_Up", LFUN_UP);
356 kbmap->bind("KP_Down", LFUN_DOWN);
357 kbmap->bind("KP_Home", LFUN_HOME);
358 kbmap->bind("KP_End", LFUN_END);
359 kbmap->bind("KP_Prior", LFUN_PRIOR);
360 kbmap->bind("KP_Next", LFUN_NEXT);
362 kbmap->bind("C-Tab", LFUN_CELL_SPLIT); // ale970515
363 kbmap->bind("S-Tab", LFUN_CELL_BACKWARD); // jug20000522
364 kbmap->bind("S-ISO_Left_Tab", LFUN_CELL_BACKWARD); // jbl 2001-23-02
368 void LyX::emergencyCleanup()
370 // what to do about tmpfiles is non-obvious. we would
371 // like to delete any we find, but our lyxdir might
372 // contain documents etc. which might be helpful on
375 bufferlist.emergencyWriteAll();
377 lyxserver->emergencyCleanup();
381 void LyX::deadKeyBindings(kb_keymap * kbmap)
383 // bindKeyings for transparent handling of deadkeys
384 // The keysyms are gotten from XFree86 X11R6
385 kbmap->bind("~C-~S-~M-dead_acute", LFUN_ACUTE);
386 kbmap->bind("~C-~S-~M-dead_breve", LFUN_BREVE);
387 kbmap->bind("~C-~S-~M-dead_caron", LFUN_CARON);
388 kbmap->bind("~C-~S-~M-dead_cedilla", LFUN_CEDILLA);
389 kbmap->bind("~C-~S-~M-dead_abovering", LFUN_CIRCLE);
390 kbmap->bind("~C-~S-~M-dead_circumflex", LFUN_CIRCUMFLEX);
391 kbmap->bind("~C-~S-~M-dead_abovedot", LFUN_DOT);
392 kbmap->bind("~C-~S-~M-dead_grave", LFUN_GRAVE);
393 kbmap->bind("~C-~S-~M-dead_doubleacute", LFUN_HUNG_UMLAUT);
394 kbmap->bind("~C-~S-~M-dead_macron", LFUN_MACRON);
395 // nothing with this name
396 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_SPECIAL_CARON);
397 kbmap->bind("~C-~S-~M-dead_tilde", LFUN_TILDE);
398 kbmap->bind("~C-~S-~M-dead_diaeresis", LFUN_UMLAUT);
399 // nothing with this name either...
400 //kbmap->bind("~C-~S-~M-dead_underbar", LFUN_UNDERBAR);
401 kbmap->bind("~C-~S-~M-dead_belowdot", LFUN_UNDERDOT);
402 kbmap->bind("~C-~S-~M-dead_tie", LFUN_TIE);
403 kbmap->bind("~C-~S-~M-dead_ogonek", LFUN_OGONEK);
407 void LyX::queryUserLyXDir(bool explicit_userdir)
409 string const configure_script = AddName(system_lyxdir(), "configure");
411 // Does user directory exist?
412 FileInfo fileInfo(user_lyxdir());
413 if (fileInfo.isOK() && fileInfo.isDir()) {
415 FileInfo script(configure_script);
416 FileInfo defaults(AddName(user_lyxdir(), "lyxrc.defaults"));
417 if (defaults.isOK() && script.isOK()
418 && defaults.getModificationTime() < script.getModificationTime()) {
419 lyxerr << _("LyX: reconfiguring user directory")
421 Path p(user_lyxdir());
422 ::system(configure_script.c_str());
423 lyxerr << "LyX: " << _("Done!") << endl;
428 first_start = !explicit_userdir;
430 lyxerr << bformat(_("LyX: Creating directory %1$s"
431 " and running configure..."), user_lyxdir()) << endl;
433 if (!createDirectory(user_lyxdir(), 0755)) {
434 // Failed, let's use $HOME instead.
435 user_lyxdir(GetEnvPath("HOME"));
436 lyxerr << bformat(_("Failed. Will use %1$s instead."),
437 user_lyxdir()) << endl;
441 // Run configure in user lyx directory
442 Path p(user_lyxdir());
443 ::system(configure_script.c_str());
444 lyxerr << "LyX: " << _("Done!") << endl;
448 void LyX::readRcFile(string const & name)
450 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
452 string const lyxrc_path = LibFileSearch(string(), name);
453 if (!lyxrc_path.empty()) {
455 lyxerr[Debug::INIT] << "Found " << name
456 << " in " << lyxrc_path << endl;
458 if (lyxrc.read(lyxrc_path) >= 0)
466 // Read the ui file `name'
467 void LyX::readUIFile(string const & name)
477 struct keyword_item uitags[ui_last - 1] = {
478 { "include", ui_include },
479 { "menuset", ui_menuset },
480 { "toolbar", ui_toolbar },
481 { "toolbars", ui_toolbars }
484 // Ensure that a file is read only once (prevents include loops)
485 static std::list<string> uifiles;
486 std::list<string>::const_iterator it = uifiles.begin();
487 std::list<string>::const_iterator end = uifiles.end();
488 it = std::find(it, end, name);
490 lyxerr[Debug::INIT] << "UI file '" << name
491 << "' has been read already. "
492 << "Is this an include loop?"
497 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
499 string const ui_path = LibFileSearch("ui", name, "ui");
501 if (ui_path.empty()) {
502 lyxerr[Debug::INIT] << "Could not find " << name << endl;
506 uifiles.push_back(name);
508 lyxerr[Debug::INIT] << "Found " << name
509 << " in " << ui_path << endl;
510 LyXLex lex(uitags, ui_last - 1);
511 lex.setFile(ui_path);
513 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
517 if (lyxerr.debugging(Debug::PARSER))
518 lex.printTable(lyxerr);
524 string const file = lex.getString();
529 menubackend.read(lex);
533 toolbarbackend.read(lex);
537 toolbarbackend.readToolbars(lex);
541 if (!rtrim(lex.getString()).empty())
542 lex.printError("LyX::ReadUIFile: "
543 "Unknown menu tag: `$$Token'");
550 // Read the languages file `name'
551 void LyX::readLanguagesFile(string const & name)
553 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
555 string const lang_path = LibFileSearch(string(), name);
556 if (lang_path.empty()) {
560 languages.read(lang_path);
564 // Read the encodings file `name'
565 void LyX::readEncodingsFile(string const & name)
567 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
569 string const enc_path = LibFileSearch(string(), name);
570 if (enc_path.empty()) {
574 encodings.read(enc_path);
583 /// return the the number of arguments consumed
584 typedef boost::function<int(string const &, string const &)> cmd_helper;
586 int parse_dbg(string const & arg, string const &)
589 lyxerr << _("List of supported debug flags:") << endl;
590 Debug::showTags(lyxerr);
593 lyxerr << bformat(_("Setting debug level to %1$s"), arg) << endl;
595 lyxerr.level(Debug::value(arg));
596 Debug::showLevel(lyxerr, lyxerr.level());
601 int parse_help(string const &, string const &)
604 _("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
605 "Command line switches (case sensitive):\n"
606 "\t-help summarize LyX usage\n"
607 "\t-userdir dir try to set user directory to dir\n"
608 "\t-sysdir dir try to set system directory to dir\n"
609 "\t-geometry WxH+X+Y set geometry of the main window\n"
610 "\t-dbg feature[,feature]...\n"
611 " select the features to debug.\n"
612 " Type `lyx -dbg' to see the list of features\n"
613 "\t-x [--execute] command\n"
614 " where command is a lyx command.\n"
615 "\t-e [--export] fmt\n"
616 " where fmt is the export format of choice.\n"
617 "\t-i [--import] fmt file.xxx\n"
618 " where fmt is the import format of choice\n"
619 " and file.xxx is the file to be imported.\n"
620 "\t-version summarize version and build info\n"
621 "Check the LyX man page for more details.") << endl;
626 int parse_version(string const &, string const &)
628 lyxerr << "LyX " << lyx_version
629 << " of " << lyx_release_date << endl;
630 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
632 lyxerr << lyx_version_info << endl;
637 int parse_sysdir(string const & arg, string const &)
640 lyxerr << _("Missing directory for -sysdir switch") << endl;
647 int parse_userdir(string const & arg, string const &)
650 lyxerr << _("Missing directory for -userdir switch") << endl;
657 int parse_execute(string const & arg, string const &)
660 lyxerr << _("Missing command string after --execute switch") << endl;
664 // Argh. Setting gui to false segfaults..
665 // FIXME: when ? how ?
670 int parse_export(string const & type, string const &)
673 lyxerr << _("Missing file type [eg latex, ps...] after "
674 "--export switch") << endl;
677 batch = "buffer-export " + type;
682 int parse_import(string const & type, string const & file)
685 lyxerr << _("Missing file type [eg latex, ps...] after "
686 "--import switch") << endl;
690 lyxerr << _("Missing filename for --import") << endl;
694 batch = "buffer-import " + type + ' ' + file;
701 bool LyX::easyParse(int & argc, char * argv[])
703 std::map<string, cmd_helper> cmdmap;
705 cmdmap["-dbg"] = parse_dbg;
706 cmdmap["-help"] = parse_help;
707 cmdmap["--help"] = parse_help;
708 cmdmap["-version"] = parse_version;
709 cmdmap["--version"] = parse_version;
710 cmdmap["-sysdir"] = parse_sysdir;
711 cmdmap["-userdir"] = parse_userdir;
712 cmdmap["-x"] = parse_execute;
713 cmdmap["--execute"] = parse_execute;
714 cmdmap["-e"] = parse_export;
715 cmdmap["--export"] = parse_export;
716 cmdmap["-i"] = parse_import;
717 cmdmap["--import"] = parse_import;
719 for (int i = 1; i < argc; ++i) {
720 std::map<string, cmd_helper>::const_iterator it
721 = cmdmap.find(argv[i]);
723 // don't complain if not found - may be parsed later
724 if (it == cmdmap.end())
727 string arg((i + 1 < argc) ? argv[i + 1] : "");
728 string arg2((i + 2 < argc) ? argv[i + 2] : "");
730 int const remove = 1 + it->second(arg, arg2);
732 // Now, remove used arguments by shifting
733 // the following ones remove places down.
735 for (int j = i; j < argc; ++j)
736 argv[j] = argv[j + remove];
740 batch_command = batch;