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 "ConverterCache.h"
23 #include "buffer_funcs.h"
24 #include "BufferList.h"
25 #include "Converter.h"
26 #include "CutAndPaste.h"
29 #include "ErrorList.h"
36 #include "LyXAction.h"
40 #include "ModuleList.h"
42 #include "ServerSocket.h"
43 #include "TextClassList.h"
44 #include "MenuBackend.h"
47 #include "ToolbarBackend.h"
49 #include "frontends/alert.h"
50 #include "frontends/Application.h"
51 #include "frontends/Gui.h"
52 #include "frontends/LyXView.h"
54 #include "support/environment.h"
55 #include "support/filetools.h"
56 #include "support/lstrings.h"
57 #include "support/lyxlib.h"
58 #include "support/ExceptionMessage.h"
59 #include "support/os.h"
60 #include "support/Package.h"
61 #include "support/Path.h"
62 #include "support/Systemcall.h"
64 #include <boost/bind.hpp>
65 #include <boost/scoped_ptr.hpp>
81 #ifndef CXX_GLOBAL_CSTD
89 using support::addName;
90 using support::addPath;
91 using support::bformat;
92 using support::changeExtension;
93 using support::createLyXTmpDir;
94 using support::FileName;
95 using support::fileSearch;
96 using support::getEnv;
97 using support::i18nLibFileSearch;
98 using support::libFileSearch;
99 using support::package;
100 using support::prependEnvPath;
101 using support::rtrim;
102 using support::Systemcall;
103 using frontend::LyXView;
105 namespace Alert = frontend::Alert;
106 namespace os = support::os;
110 // Are we using the GUI at all? We default to true and this is changed
111 // to false when the export feature is used.
115 bool quitting; // flag, that we are quitting the program
119 // Filled with the command line arguments "foo" of "-sysdir foo" or
121 string cl_system_support;
122 string cl_user_support;
124 std::string geometryArg;
126 LyX * singleton_ = 0;
128 void showFileError(string const & error)
130 Alert::warning(_("Could not read configuration file"),
131 bformat(_("Error while reading the configuration file\n%1$s.\n"
132 "Please check your installation."), from_utf8(error)));
136 void reconfigureUserLyXDir()
138 string const configure_command = package().configure_command();
140 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
141 support::PathChanger p(package().user_support());
143 one.startscript(Systemcall::Wait, configure_command);
144 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
150 /// The main application class private implementation.
155 // Set the default User Interface language as soon as possible.
156 // The language used will be derived from the environment
158 messages_["GUI"] = Messages();
160 /// our function handler
163 BufferList buffer_list_;
165 boost::scoped_ptr<KeyMap> toplevel_keymap_;
167 boost::scoped_ptr<CmdDef> toplevel_cmddef_;
169 boost::scoped_ptr<Server> lyx_server_;
171 boost::scoped_ptr<ServerSocket> lyx_socket_;
173 boost::scoped_ptr<frontend::Application> application_;
174 /// lyx session, containing lastfiles, lastfilepos, and lastopened
175 boost::scoped_ptr<Session> session_;
177 /// Files to load at start.
178 vector<FileName> files_to_load_;
180 /// The messages translators.
181 map<string, Messages> messages_;
183 /// The file converters.
184 Converters converters_;
186 // The system converters copy after reading lyxrc.defaults.
187 Converters system_converters_;
192 Movers system_movers_;
194 /// has this user started lyx for the first time?
196 /// the parsed command line batch command if any
197 std::string batch_command;
201 frontend::Application * theApp()
204 return singleton_->pimpl_->application_.get();
218 BOOST_ASSERT(singleton_);
223 LyX const & LyX::cref()
225 BOOST_ASSERT(singleton_);
238 BufferList & LyX::bufferList()
240 return pimpl_->buffer_list_;
244 BufferList const & LyX::bufferList() const
246 return pimpl_->buffer_list_;
250 Session & LyX::session()
252 BOOST_ASSERT(pimpl_->session_.get());
253 return *pimpl_->session_.get();
257 Session const & LyX::session() const
259 BOOST_ASSERT(pimpl_->session_.get());
260 return *pimpl_->session_.get();
264 LyXFunc & LyX::lyxFunc()
266 return pimpl_->lyxfunc_;
270 LyXFunc const & LyX::lyxFunc() const
272 return pimpl_->lyxfunc_;
276 Server & LyX::server()
278 BOOST_ASSERT(pimpl_->lyx_server_.get());
279 return *pimpl_->lyx_server_.get();
283 Server const & LyX::server() const
285 BOOST_ASSERT(pimpl_->lyx_server_.get());
286 return *pimpl_->lyx_server_.get();
290 ServerSocket & LyX::socket()
292 BOOST_ASSERT(pimpl_->lyx_socket_.get());
293 return *pimpl_->lyx_socket_.get();
297 ServerSocket const & LyX::socket() const
299 BOOST_ASSERT(pimpl_->lyx_socket_.get());
300 return *pimpl_->lyx_socket_.get();
304 frontend::Application & LyX::application()
306 BOOST_ASSERT(pimpl_->application_.get());
307 return *pimpl_->application_.get();
311 frontend::Application const & LyX::application() const
313 BOOST_ASSERT(pimpl_->application_.get());
314 return *pimpl_->application_.get();
318 KeyMap & LyX::topLevelKeymap()
320 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
321 return *pimpl_->toplevel_keymap_.get();
325 CmdDef & LyX::topLevelCmdDef()
327 BOOST_ASSERT(pimpl_->toplevel_cmddef_.get());
328 return *pimpl_->toplevel_cmddef_.get();
332 Converters & LyX::converters()
334 return pimpl_->converters_;
338 Converters & LyX::systemConverters()
340 return pimpl_->system_converters_;
344 KeyMap const & LyX::topLevelKeymap() const
346 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
347 return *pimpl_->toplevel_keymap_.get();
351 Messages & LyX::getMessages(std::string const & language)
353 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
355 if (it != pimpl_->messages_.end())
358 std::pair<map<string, Messages>::iterator, bool> result =
359 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
361 BOOST_ASSERT(result.second);
362 return result.first->second;
366 Messages & LyX::getGuiMessages()
368 return pimpl_->messages_["GUI"];
372 void LyX::setGuiLanguage(std::string const & language)
374 pimpl_->messages_["GUI"] = Messages(language);
378 Buffer const * LyX::updateInset(Inset const * inset) const
380 if (quitting || !inset)
382 return application().gui().updateInset(inset);
386 void LyX::hideDialogs(std::string const & name, Inset * inset) const
388 if (quitting || !use_gui)
390 application().gui().hideDialogs(name, inset);
394 int LyX::exec(int & argc, char * argv[])
396 // Here we need to parse the command line. At least
397 // we need to parse for "-dbg" and "-help"
398 easyParse(argc, argv);
401 support::init_package(to_utf8(from_local8bit(argv[0])),
402 cl_system_support, cl_user_support,
403 support::top_build_dir_is_one_level_up);
404 } catch (support::ExceptionMessage const & message) {
405 if (message.type_ == support::ErrorException) {
406 Alert::error(message.title_, message.details_);
408 } else if (message.type_ == support::WarningException) {
409 Alert::warning(message.title_, message.details_);
413 // Reinit the messages machinery in case package() knows
414 // something interesting about the locale directory.
418 // FIXME: create a ConsoleApplication
419 int exit_status = init(argc, argv);
427 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
432 BufferList::iterator begin = pimpl_->buffer_list_.begin();
434 bool final_success = false;
435 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
437 if (buf != buf->masterBuffer())
439 bool success = false;
440 buf->dispatch(pimpl_->batch_command, &success);
441 final_success |= success;
444 return !final_success;
447 // Let the frontend parse and remove all arguments that it knows
448 pimpl_->application_.reset(createApplication(argc, argv));
452 // Parse and remove all known arguments in the LyX singleton
453 // Give an error for all remaining ones.
454 int exit_status = init(argc, argv);
456 // Kill the application object before exiting.
457 pimpl_->application_.reset();
464 /* Create a CoreApplication class that will provide the main event loop
465 * and the socket callback registering. With Qt4, only QtCore
466 * library would be needed.
467 * When this is done, a server_mode could be created and the following two
468 * line would be moved out from here.
470 // Note: socket callback must be registered after init(argc, argv)
471 // such that package().temp_dir() is properly initialized.
472 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
473 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
474 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
476 // Start the real execution loop.
477 exit_status = pimpl_->application_->exec();
485 void LyX::prepareExit()
487 // Clear the clipboard and selection stack:
488 cap::clearCutStack();
489 cap::clearSelection();
491 // Set a flag that we do quitting from the program,
492 // so no refreshes are necessary.
495 // close buffers first
496 pimpl_->buffer_list_.closeAll();
498 // do any other cleanup procedures now
499 if (package().temp_dir() != package().system_temp_dir()) {
500 LYXERR(Debug::INFO, "Deleting tmp dir "
501 << package().temp_dir().absFilename());
503 if (!package().temp_dir().destroyDirectory()) {
504 docstring const msg =
505 bformat(_("Unable to remove the temporary directory %1$s"),
506 from_utf8(package().temp_dir().absFilename()));
507 Alert::warning(_("Unable to remove temporary directory"), msg);
512 if (pimpl_->session_)
513 pimpl_->session_->writeFile();
514 pimpl_->session_.reset();
515 pimpl_->lyx_server_.reset();
516 pimpl_->lyx_socket_.reset();
519 // Kill the application object before exiting. This avoids crashes
520 // when exiting on Linux.
521 if (pimpl_->application_)
522 pimpl_->application_.reset();
526 void LyX::earlyExit(int status)
528 BOOST_ASSERT(pimpl_->application_.get());
529 // LyX::pimpl_::application_ is not initialised at this
530 // point so it's safe to just exit after some cleanup.
536 int LyX::init(int & argc, char * argv[])
538 // check for any spurious extra arguments
539 // other than documents
540 for (int argi = 1; argi < argc ; ++argi) {
541 if (argv[argi][0] == '-') {
543 bformat(_("Wrong command line option `%1$s'. Exiting."),
544 from_utf8(argv[argi]))) << endl;
549 // Initialization of LyX (reads lyxrc and more)
550 LYXERR(Debug::INIT, "Initializing LyX::init...");
551 bool success = init();
552 LYXERR(Debug::INIT, "Initializing LyX::init...done");
556 for (int argi = argc - 1; argi >= 1; --argi) {
557 // get absolute path of file and add ".lyx" to
558 // the filename if necessary
559 pimpl_->files_to_load_.push_back(fileSearch(string(),
560 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
561 "lyx", support::allow_unreadable));
565 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
571 void LyX::addFileToLoad(FileName const & fname)
573 vector<FileName>::const_iterator cit = std::find(
574 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
577 if (cit == pimpl_->files_to_load_.end())
578 pimpl_->files_to_load_.push_back(fname);
582 void LyX::loadFiles()
584 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
585 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
587 for (; it != end; ++it) {
591 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
592 if (buf->loadLyXFile(*it)) {
593 ErrorList const & el = buf->errorList("Parse");
595 for_each(el.begin(), el.end(),
596 boost::bind(&LyX::printError, this, _1));
599 pimpl_->buffer_list_.release(buf);
604 void LyX::execBatchCommands()
606 // The advantage of doing this here is that the event loop
607 // is already started. So any need for interaction will be
611 // if reconfiguration is needed.
612 if (textclasslist.empty()) {
613 switch (Alert::prompt(
614 _("No textclass is found"),
615 _("LyX cannot continue because no textclass is found. "
616 "You can either reconfigure normally, or reconfigure using "
617 "default textclasses, or quit LyX."),
624 // regular reconfigure
625 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
628 // reconfigure --without-latex-config
629 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
630 " --without-latex-config"));
633 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
637 // Execute batch commands if available
638 if (pimpl_->batch_command.empty())
641 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
643 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
647 void LyX::restoreGuiSession()
649 LyXView * view = newLyXView();
651 // if there is no valid class list, do not load any file.
652 if (textclasslist.empty())
655 // if some files were specified at command-line we assume that the
656 // user wants to edit *these* files and not to restore the session.
657 if (!pimpl_->files_to_load_.empty()) {
658 for_each(pimpl_->files_to_load_.begin(),
659 pimpl_->files_to_load_.end(),
660 bind(&LyXView::loadLyXFile, view, _1, true));
661 // clear this list to save a few bytes of RAM
662 pimpl_->files_to_load_.clear();
663 pimpl_->session_->lastOpened().clear();
665 } else if (lyxrc.load_session) {
666 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
667 // do not add to the lastfile list since these files are restored from
668 // last session, and should be already there (regular files), or should
669 // not be added at all (help files).
670 for_each(lastopened.begin(), lastopened.end(),
671 bind(&LyXView::loadLyXFile, view, _1, false));
673 // clear this list to save a few bytes of RAM
674 pimpl_->session_->lastOpened().clear();
677 BufferList::iterator I = pimpl_->buffer_list_.begin();
678 BufferList::iterator end = pimpl_->buffer_list_.end();
679 for (; I != end; ++I) {
681 if (buf != buf->masterBuffer())
686 // FIXME: Switch to the last loaded Buffer. This must not be the first one
687 // because the Buffer won't be connected in this case. The correct solution
688 // would be to avoid the manual connection of the current Buffer in LyXView.
689 if (!pimpl_->buffer_list_.empty())
690 view->setBuffer(pimpl_->buffer_list_.last());
694 LyXView * LyX::newLyXView()
699 // create the main window
700 LyXView * view = &pimpl_->application_->createView(geometryArg);
708 The SIGHUP signal does not exist on Windows and does not need to be handled.
710 Windows handles SIGFPE and SIGSEGV signals as expected.
712 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
713 cause a new thread to be spawned. This may well result in unexpected
714 behaviour by the single-threaded LyX.
716 SIGTERM signals will come only from another process actually sending
717 that signal using 'raise' in Windows' POSIX compatability layer. It will
718 not come from the general "terminate process" methods that everyone
719 actually uses (and which can't be trapped). Killing an app 'politely' on
720 Windows involves first sending a WM_CLOSE message, something that is
721 caught already by the Qt frontend.
723 For more information see:
725 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
726 ...signals are mostly useless on Windows for a variety of reasons that are
729 'UNIX Application Migration Guide, Chapter 9'
730 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
732 'How To Terminate an Application "Cleanly" in Win32'
733 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
737 static void error_handler(int err_sig)
739 // Throw away any signals other than the first one received.
740 static sig_atomic_t handling_error = false;
743 handling_error = true;
745 // We have received a signal indicating a fatal error, so
746 // try and save the data ASAP.
747 LyX::cref().emergencyCleanup();
749 // These lyxerr calls may or may not work:
751 // Signals are asynchronous, so the main program may be in a very
752 // fragile state when a signal is processed and thus while a signal
753 // handler function executes.
754 // In general, therefore, we should avoid performing any
755 // I/O operations or calling most library and system functions from
758 // This shouldn't matter here, however, as we've already invoked
763 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
767 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
770 lyxerr << "\nlyx: SIGSEGV signal caught\n"
771 "Sorry, you have found a bug in LyX. "
772 "Please read the bug-reporting instructions "
773 "in Help->Introduction and send us a bug report, "
774 "if necessary. Thanks !\nBye." << endl;
782 // Deinstall the signal handlers
784 signal(SIGHUP, SIG_DFL);
786 signal(SIGINT, SIG_DFL);
787 signal(SIGFPE, SIG_DFL);
788 signal(SIGSEGV, SIG_DFL);
789 signal(SIGTERM, SIG_DFL);
792 if (err_sig == SIGSEGV ||
793 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
795 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
804 void LyX::printError(ErrorItem const & ei)
806 docstring tmp = _("LyX: ") + ei.error + char_type(':')
808 std::cerr << to_utf8(tmp) << std::endl;
812 void LyX::initGuiFont()
814 if (lyxrc.roman_font_name.empty())
815 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
817 if (lyxrc.sans_font_name.empty())
818 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
820 if (lyxrc.typewriter_font_name.empty())
821 lyxrc.typewriter_font_name
822 = pimpl_->application_->typewriterFontName();
829 signal(SIGHUP, error_handler);
831 signal(SIGFPE, error_handler);
832 signal(SIGSEGV, error_handler);
833 signal(SIGINT, error_handler);
834 signal(SIGTERM, error_handler);
835 // SIGPIPE can be safely ignored.
837 lyxrc.tempdir_path = package().temp_dir().absFilename();
838 lyxrc.document_path = package().document_dir().absFilename();
840 if (lyxrc.template_path.empty()) {
841 lyxrc.template_path = addPath(package().system_support().absFilename(),
846 // Read configuration files
849 // This one may have been distributed along with LyX.
850 if (!readRcFile("lyxrc.dist"))
853 // Set the language defined by the distributor.
854 //setGuiLanguage(lyxrc.gui_language);
856 // Set the PATH correctly.
857 #if !defined (USE_POSIX_PACKAGING)
858 // Add the directory containing the LyX executable to the path
859 // so that LyX can find things like tex2lyx.
860 if (package().build_support().empty())
861 prependEnvPath("PATH", package().binary_dir().absFilename());
863 if (!lyxrc.path_prefix.empty())
864 prependEnvPath("PATH", lyxrc.path_prefix);
866 // Check that user LyX directory is ok.
867 if (queryUserLyXDir(package().explicit_user_support()))
868 reconfigureUserLyXDir();
870 // no need for a splash when there is no GUI
875 // This one is generated in user_support directory by lib/configure.py.
876 if (!readRcFile("lyxrc.defaults"))
879 // Query the OS to know what formats are viewed natively
880 formats.setAutoOpen();
882 // Read lyxrc.dist again to be able to override viewer auto-detection.
883 readRcFile("lyxrc.dist");
885 system_lyxrc = lyxrc;
886 system_formats = formats;
887 pimpl_->system_converters_ = pimpl_->converters_;
888 pimpl_->system_movers_ = pimpl_->movers_;
889 system_lcolor = lcolor;
891 // This one is edited through the preferences dialog.
892 if (!readRcFile("preferences"))
895 if (!readEncodingsFile("encodings", "unicodesymbols"))
897 if (!readLanguagesFile("languages"))
901 LYXERR(Debug::INIT, "Reading layouts...");
907 // read keymap and ui files in batch mode as well
908 // because InsetInfo needs to know these to produce
909 // the correct output
911 // Set the language defined by the user.
912 //setGuiLanguage(lyxrc.gui_language);
914 // Set up command definitions
915 pimpl_->toplevel_cmddef_.reset(new CmdDef);
916 pimpl_->toplevel_cmddef_->read(lyxrc.def_file);
919 pimpl_->toplevel_keymap_.reset(new KeyMap);
920 pimpl_->toplevel_keymap_->read("site");
921 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
922 // load user bind file user.bind
923 pimpl_->toplevel_keymap_->read("user");
925 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
928 if (!readUIFile(lyxrc.ui_file))
931 if (lyxerr.debugging(Debug::LYXRC))
934 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
935 if (!lyxrc.path_prefix.empty())
936 prependEnvPath("PATH", lyxrc.path_prefix);
938 FileName const document_path(lyxrc.document_path);
939 if (document_path.exists() && document_path.isDirectory())
940 package().document_dir() = document_path;
942 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
943 if (package().temp_dir().empty()) {
944 Alert::error(_("Could not create temporary directory"),
945 bformat(_("Could not create a temporary directory in\n"
946 "%1$s. Make sure that this\n"
947 "path exists and is writable and try again."),
948 from_utf8(lyxrc.tempdir_path)));
949 // createLyXTmpDir() tries sufficiently hard to create a
950 // usable temp dir, so the probability to come here is
951 // close to zero. We therefore don't try to overcome this
952 // problem with e.g. asking the user for a new path and
953 // trying again but simply exit.
957 LYXERR(Debug::INIT, "LyX tmp dir: `"
958 << package().temp_dir().absFilename() << '\'');
960 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
961 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
963 // This must happen after package initialization and after lyxrc is
964 // read, therefore it can't be done by a static object.
965 ConverterCache::init();
971 void LyX::emergencyCleanup() const
973 // what to do about tmpfiles is non-obvious. we would
974 // like to delete any we find, but our lyxdir might
975 // contain documents etc. which might be helpful on
978 pimpl_->buffer_list_.emergencyWriteAll();
980 if (pimpl_->lyx_server_)
981 pimpl_->lyx_server_->emergencyCleanup();
982 pimpl_->lyx_server_.reset();
983 pimpl_->lyx_socket_.reset();
988 void LyX::deadKeyBindings(KeyMap * kbmap)
990 // bindKeyings for transparent handling of deadkeys
991 // The keysyms are gotten from XFree86 X11R6
992 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
993 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
994 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
995 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
996 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
997 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
998 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
999 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1000 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1001 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1002 // nothing with this name
1003 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1004 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1005 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1006 // nothing with this name either...
1007 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1008 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1009 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1010 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1014 // return true if file does not exist or is older than configure.py.
1015 static bool needsUpdate(string const & file)
1017 // We cannot initialize configure_script directly because the package
1018 // is not initialized yet when static objects are constructed.
1019 static FileName configure_script;
1020 static bool firstrun = true;
1023 FileName(addName(package().system_support().absFilename(),
1029 FileName(addName(package().user_support().absFilename(), file));
1030 return !absfile.exists()
1031 || configure_script.lastModified() > absfile.lastModified();
1035 bool LyX::queryUserLyXDir(bool explicit_userdir)
1037 // Does user directory exist?
1038 FileName const sup = package().user_support();
1039 if (sup.exists() && sup.isDirectory()) {
1040 first_start = false;
1042 return needsUpdate("lyxrc.defaults")
1043 || needsUpdate("lyxmodules.lst")
1044 || needsUpdate("textclass.lst")
1045 || needsUpdate("packages.lst");
1048 first_start = !explicit_userdir;
1050 // If the user specified explicitly a directory, ask whether
1051 // to create it. If the user says "no", then exit.
1052 if (explicit_userdir &&
1054 _("Missing user LyX directory"),
1055 bformat(_("You have specified a non-existent user "
1056 "LyX directory, %1$s.\n"
1057 "It is needed to keep your own configuration."),
1058 from_utf8(package().user_support().absFilename())),
1060 _("&Create directory"),
1062 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1063 earlyExit(EXIT_FAILURE);
1066 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1067 from_utf8(sup.absFilename()))) << endl;
1069 if (!sup.createDirectory(0755)) {
1070 // Failed, so let's exit.
1071 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1073 earlyExit(EXIT_FAILURE);
1080 bool LyX::readRcFile(string const & name)
1082 LYXERR(Debug::INIT, "About to read " << name << "... ");
1084 FileName const lyxrc_path = libFileSearch(string(), name);
1085 if (!lyxrc_path.empty()) {
1086 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1087 if (lyxrc.read(lyxrc_path) < 0) {
1088 showFileError(name);
1092 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1098 // Read the ui file `name'
1099 bool LyX::readUIFile(string const & name, bool include)
1109 struct keyword_item uitags[ui_last - 1] = {
1110 { "include", ui_include },
1111 { "menuset", ui_menuset },
1112 { "toolbars", ui_toolbars },
1113 { "toolbarset", ui_toolbarset }
1116 // Ensure that a file is read only once (prevents include loops)
1117 static std::list<string> uifiles;
1118 std::list<string>::const_iterator it = uifiles.begin();
1119 std::list<string>::const_iterator end = uifiles.end();
1120 it = std::find(it, end, name);
1122 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1123 << "Is this an include loop?");
1127 LYXERR(Debug::INIT, "About to read " << name << "...");
1132 ui_path = libFileSearch("ui", name, "inc");
1133 if (ui_path.empty())
1134 ui_path = libFileSearch("ui",
1135 changeExtension(name, "inc"));
1138 ui_path = libFileSearch("ui", name, "ui");
1140 if (ui_path.empty()) {
1141 LYXERR(Debug::INIT, "Could not find " << name);
1142 showFileError(name);
1146 uifiles.push_back(name);
1148 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1149 Lexer lex(uitags, ui_last - 1);
1150 lex.setFile(ui_path);
1152 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1156 if (lyxerr.debugging(Debug::PARSER))
1157 lex.printTable(lyxerr);
1159 while (lex.isOK()) {
1160 switch (lex.lex()) {
1163 string const file = lex.getString();
1164 if (!readUIFile(file, true))
1169 menubackend.read(lex);
1173 toolbarbackend.readToolbars(lex);
1177 toolbarbackend.readToolbarSettings(lex);
1181 if (!rtrim(lex.getString()).empty())
1182 lex.printError("LyX::ReadUIFile: "
1183 "Unknown menu tag: `$$Token'");
1191 // Read the languages file `name'
1192 bool LyX::readLanguagesFile(string const & name)
1194 LYXERR(Debug::INIT, "About to read " << name << "...");
1196 FileName const lang_path = libFileSearch(string(), name);
1197 if (lang_path.empty()) {
1198 showFileError(name);
1201 languages.read(lang_path);
1206 // Read the encodings file `name'
1207 bool LyX::readEncodingsFile(string const & enc_name,
1208 string const & symbols_name)
1210 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1211 << symbols_name << "...");
1213 FileName const symbols_path = libFileSearch(string(), symbols_name);
1214 if (symbols_path.empty()) {
1215 showFileError(symbols_name);
1219 FileName const enc_path = libFileSearch(string(), enc_name);
1220 if (enc_path.empty()) {
1221 showFileError(enc_name);
1224 encodings.read(enc_path, symbols_path);
1233 /// return the the number of arguments consumed
1234 typedef boost::function<int(string const &, string const &)> cmd_helper;
1236 int parse_dbg(string const & arg, string const &)
1239 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1240 Debug::showTags(lyxerr);
1243 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1245 lyxerr.level(Debug::value(arg));
1246 Debug::showLevel(lyxerr, lyxerr.level());
1251 int parse_help(string const &, string const &)
1254 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1255 "Command line switches (case sensitive):\n"
1256 "\t-help summarize LyX usage\n"
1257 "\t-userdir dir set user directory to dir\n"
1258 "\t-sysdir dir set system directory to dir\n"
1259 "\t-geometry WxH+X+Y set geometry of the main window\n"
1260 "\t-dbg feature[,feature]...\n"
1261 " select the features to debug.\n"
1262 " Type `lyx -dbg' to see the list of features\n"
1263 "\t-x [--execute] command\n"
1264 " where command is a lyx command.\n"
1265 "\t-e [--export] fmt\n"
1266 " where fmt is the export format of choice.\n"
1267 " Look on Tools->Preferences->File formats->Format\n"
1268 " to get an idea which parameters should be passed.\n"
1269 "\t-i [--import] fmt file.xxx\n"
1270 " where fmt is the import format of choice\n"
1271 " and file.xxx is the file to be imported.\n"
1272 "\t-version summarize version and build info\n"
1273 "Check the LyX man page for more details.")) << endl;
1279 int parse_version(string const &, string const &)
1281 lyxerr << "LyX " << lyx_version
1282 << " (" << lyx_release_date << ")" << endl;
1283 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1285 lyxerr << lyx_version_info << endl;
1291 int parse_sysdir(string const & arg, string const &)
1294 Alert::error(_("No system directory"),
1295 _("Missing directory for -sysdir switch"));
1298 cl_system_support = arg;
1303 int parse_userdir(string const & arg, string const &)
1306 Alert::error(_("No user directory"),
1307 _("Missing directory for -userdir switch"));
1310 cl_user_support = arg;
1315 int parse_execute(string const & arg, string const &)
1318 Alert::error(_("Incomplete command"),
1319 _("Missing command string after --execute switch"));
1327 int parse_export(string const & type, string const &)
1330 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1331 "--export switch")) << endl;
1334 batch = "buffer-export " + type;
1340 int parse_import(string const & type, string const & file)
1343 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1344 "--import switch")) << endl;
1348 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1352 batch = "buffer-import " + type + ' ' + file;
1357 int parse_geometry(string const & arg1, string const &)
1360 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1361 // remove also the arg
1364 // don't remove "-geometry"
1373 void LyX::easyParse(int & argc, char * argv[])
1375 std::map<string, cmd_helper> cmdmap;
1377 cmdmap["-dbg"] = parse_dbg;
1378 cmdmap["-help"] = parse_help;
1379 cmdmap["--help"] = parse_help;
1380 cmdmap["-version"] = parse_version;
1381 cmdmap["--version"] = parse_version;
1382 cmdmap["-sysdir"] = parse_sysdir;
1383 cmdmap["-userdir"] = parse_userdir;
1384 cmdmap["-x"] = parse_execute;
1385 cmdmap["--execute"] = parse_execute;
1386 cmdmap["-e"] = parse_export;
1387 cmdmap["--export"] = parse_export;
1388 cmdmap["-i"] = parse_import;
1389 cmdmap["--import"] = parse_import;
1390 cmdmap["-geometry"] = parse_geometry;
1392 for (int i = 1; i < argc; ++i) {
1393 std::map<string, cmd_helper>::const_iterator it
1394 = cmdmap.find(argv[i]);
1396 // don't complain if not found - may be parsed later
1397 if (it == cmdmap.end())
1401 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1403 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1405 int const remove = 1 + it->second(arg, arg2);
1407 // Now, remove used arguments by shifting
1408 // the following ones remove places down.
1411 for (int j = i; j < argc; ++j)
1412 argv[j] = argv[j + remove];
1417 pimpl_->batch_command = batch;
1421 FuncStatus getStatus(FuncRequest const & action)
1423 return LyX::ref().lyxFunc().getStatus(action);
1427 void dispatch(FuncRequest const & action)
1429 LyX::ref().lyxFunc().dispatch(action);
1433 BufferList & theBufferList()
1435 return LyX::ref().bufferList();
1439 LyXFunc & theLyXFunc()
1441 return LyX::ref().lyxFunc();
1445 Server & theServer()
1447 // FIXME: this should not be use_gui dependent
1448 BOOST_ASSERT(use_gui);
1449 return LyX::ref().server();
1453 ServerSocket & theServerSocket()
1455 // FIXME: this should not be use_gui dependent
1456 BOOST_ASSERT(use_gui);
1457 return LyX::ref().socket();
1461 KeyMap & theTopLevelKeymap()
1463 return LyX::ref().topLevelKeymap();
1467 Converters & theConverters()
1469 return LyX::ref().converters();
1473 Converters & theSystemConverters()
1475 return LyX::ref().systemConverters();
1479 Movers & theMovers()
1481 return LyX::ref().pimpl_->movers_;
1485 Mover const & getMover(std::string const & fmt)
1487 return LyX::ref().pimpl_->movers_(fmt);
1491 void setMover(std::string const & fmt, std::string const & command)
1493 LyX::ref().pimpl_->movers_.set(fmt, command);
1497 Movers & theSystemMovers()
1499 return LyX::ref().pimpl_->system_movers_;
1503 Messages & getMessages(std::string const & language)
1505 return LyX::ref().getMessages(language);
1509 Messages & getGuiMessages()
1511 return LyX::ref().getGuiMessages();