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.
20 #include "buffer_funcs.h"
22 #include "BufferList.h"
25 #include "ConverterCache.h"
26 #include "Converter.h"
27 #include "CutAndPaste.h"
29 #include "ErrorList.h"
34 #include "LyXAction.h"
37 #include "MenuBackend.h"
38 #include "ModuleList.h"
41 #include "ServerSocket.h"
43 #include "TextClassList.h"
44 #include "ToolbarBackend.h"
46 #include "frontends/alert.h"
47 #include "frontends/Application.h"
49 #include "support/debug.h"
50 #include "support/environment.h"
51 #include "support/ExceptionMessage.h"
52 #include "support/filetools.h"
53 #include "support/gettext.h"
54 #include "support/lstrings.h"
55 #include "support/lyxlib.h"
56 #include "support/Messages.h"
57 #include "support/os.h"
58 #include "support/Package.h"
59 #include "support/Path.h"
60 #include "support/Systemcall.h"
62 #include <boost/bind.hpp>
63 #include <boost/scoped_ptr.hpp>
76 using support::addName;
77 using support::addPath;
78 using support::bformat;
79 using support::changeExtension;
80 using support::createLyXTmpDir;
81 using support::FileName;
82 using support::fileSearch;
83 using support::getEnv;
84 using support::i18nLibFileSearch;
85 using support::libFileSearch;
86 using support::package;
87 using support::prependEnvPath;
89 using support::Systemcall;
91 namespace Alert = frontend::Alert;
92 namespace os = support::os;
96 // Are we using the GUI at all? We default to true and this is changed
97 // to false when the export feature is used.
101 bool quitting; // flag, that we are quitting the program
105 // Filled with the command line arguments "foo" of "-sysdir foo" or
107 string cl_system_support;
108 string cl_user_support;
110 std::string geometryArg;
112 LyX * singleton_ = 0;
114 void showFileError(string const & error)
116 Alert::warning(_("Could not read configuration file"),
117 bformat(_("Error while reading the configuration file\n%1$s.\n"
118 "Please check your installation."), from_utf8(error)));
122 void reconfigureUserLyXDir()
124 string const configure_command = package().configure_command();
126 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
127 support::PathChanger p(package().user_support());
129 one.startscript(Systemcall::Wait, configure_command);
130 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
136 /// The main application class private implementation.
141 // Set the default User Interface language as soon as possible.
142 // The language used will be derived from the environment
144 messages_["GUI"] = Messages();
146 /// our function handler
149 BufferList buffer_list_;
151 KeyMap toplevel_keymap_;
153 CmdDef toplevel_cmddef_;
155 boost::scoped_ptr<Server> lyx_server_;
157 boost::scoped_ptr<ServerSocket> lyx_socket_;
159 boost::scoped_ptr<frontend::Application> application_;
160 /// lyx session, containing lastfiles, lastfilepos, and lastopened
161 boost::scoped_ptr<Session> session_;
163 /// Files to load at start.
164 vector<FileName> files_to_load_;
166 /// The messages translators.
167 map<string, Messages> messages_;
169 /// The file converters.
170 Converters converters_;
172 // The system converters copy after reading lyxrc.defaults.
173 Converters system_converters_;
178 Movers system_movers_;
180 /// has this user started lyx for the first time?
182 /// the parsed command line batch command if any
183 std::string batch_command;
187 frontend::Application * theApp()
190 return singleton_->pimpl_->application_.get();
204 BOOST_ASSERT(singleton_);
209 LyX const & LyX::cref()
211 BOOST_ASSERT(singleton_);
224 BufferList & LyX::bufferList()
226 return pimpl_->buffer_list_;
230 BufferList const & LyX::bufferList() const
232 return pimpl_->buffer_list_;
236 Session & LyX::session()
238 BOOST_ASSERT(pimpl_->session_.get());
239 return *pimpl_->session_.get();
243 Session const & LyX::session() const
245 BOOST_ASSERT(pimpl_->session_.get());
246 return *pimpl_->session_.get();
250 LyXFunc & LyX::lyxFunc()
252 return pimpl_->lyxfunc_;
256 LyXFunc const & LyX::lyxFunc() const
258 return pimpl_->lyxfunc_;
262 Server & LyX::server()
264 BOOST_ASSERT(pimpl_->lyx_server_.get());
265 return *pimpl_->lyx_server_.get();
269 Server const & LyX::server() const
271 BOOST_ASSERT(pimpl_->lyx_server_.get());
272 return *pimpl_->lyx_server_.get();
276 ServerSocket & LyX::socket()
278 BOOST_ASSERT(pimpl_->lyx_socket_.get());
279 return *pimpl_->lyx_socket_.get();
283 ServerSocket const & LyX::socket() const
285 BOOST_ASSERT(pimpl_->lyx_socket_.get());
286 return *pimpl_->lyx_socket_.get();
290 frontend::Application & LyX::application()
292 BOOST_ASSERT(pimpl_->application_.get());
293 return *pimpl_->application_.get();
297 frontend::Application const & LyX::application() const
299 BOOST_ASSERT(pimpl_->application_.get());
300 return *pimpl_->application_.get();
304 KeyMap & LyX::topLevelKeymap()
306 return pimpl_->toplevel_keymap_;
310 CmdDef & LyX::topLevelCmdDef()
312 return pimpl_->toplevel_cmddef_;
316 Converters & LyX::converters()
318 return pimpl_->converters_;
322 Converters & LyX::systemConverters()
324 return pimpl_->system_converters_;
328 KeyMap const & LyX::topLevelKeymap() const
330 return pimpl_->toplevel_keymap_;
334 Messages & LyX::getMessages(std::string const & language)
336 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
338 if (it != pimpl_->messages_.end())
341 std::pair<map<string, Messages>::iterator, bool> result =
342 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
344 BOOST_ASSERT(result.second);
345 return result.first->second;
349 Messages & LyX::getGuiMessages()
351 return pimpl_->messages_["GUI"];
355 void LyX::setGuiLanguage(std::string const & language)
357 pimpl_->messages_["GUI"] = Messages(language);
361 int LyX::exec(int & argc, char * argv[])
363 // Here we need to parse the command line. At least
364 // we need to parse for "-dbg" and "-help"
365 easyParse(argc, argv);
368 support::init_package(to_utf8(from_local8bit(argv[0])),
369 cl_system_support, cl_user_support,
370 support::top_build_dir_is_one_level_up);
371 } catch (support::ExceptionMessage const & message) {
372 if (message.type_ == support::ErrorException) {
373 Alert::error(message.title_, message.details_);
375 } else if (message.type_ == support::WarningException) {
376 Alert::warning(message.title_, message.details_);
380 // Reinit the messages machinery in case package() knows
381 // something interesting about the locale directory.
385 // FIXME: create a ConsoleApplication
386 int exit_status = init(argc, argv);
394 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
399 BufferList::iterator begin = pimpl_->buffer_list_.begin();
401 bool final_success = false;
402 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
404 if (buf != buf->masterBuffer())
406 bool success = false;
407 buf->dispatch(pimpl_->batch_command, &success);
408 final_success |= success;
411 return !final_success;
414 // Let the frontend parse and remove all arguments that it knows
415 pimpl_->application_.reset(createApplication(argc, argv));
417 // Parse and remove all known arguments in the LyX singleton
418 // Give an error for all remaining ones.
419 int exit_status = init(argc, argv);
421 // Kill the application object before exiting.
422 pimpl_->application_.reset();
429 /* Create a CoreApplication class that will provide the main event loop
430 * and the socket callback registering. With Qt4, only QtCore
431 * library would be needed.
432 * When this is done, a server_mode could be created and the following two
433 * line would be moved out from here.
435 // Note: socket callback must be registered after init(argc, argv)
436 // such that package().temp_dir() is properly initialized.
437 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
438 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
439 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
441 // Start the real execution loop.
442 exit_status = pimpl_->application_->exec();
450 void LyX::prepareExit()
452 // Clear the clipboard and selection stack:
453 cap::clearCutStack();
454 cap::clearSelection();
456 // Set a flag that we do quitting from the program,
457 // so no refreshes are necessary.
460 // close buffers first
461 pimpl_->buffer_list_.closeAll();
463 // register session changes and shutdown server and socket
465 if (pimpl_->session_)
466 pimpl_->session_->writeFile();
467 pimpl_->session_.reset();
468 pimpl_->lyx_server_.reset();
469 pimpl_->lyx_socket_.reset();
472 // do any other cleanup procedures now
473 if (package().temp_dir() != package().system_temp_dir()) {
474 LYXERR(Debug::INFO, "Deleting tmp dir "
475 << package().temp_dir().absFilename());
477 if (!package().temp_dir().destroyDirectory()) {
478 docstring const msg =
479 bformat(_("Unable to remove the temporary directory %1$s"),
480 from_utf8(package().temp_dir().absFilename()));
481 Alert::warning(_("Unable to remove temporary directory"), msg);
485 // Kill the application object before exiting. This avoids crashes
486 // when exiting on Linux.
487 if (pimpl_->application_)
488 pimpl_->application_.reset();
492 void LyX::earlyExit(int status)
494 BOOST_ASSERT(pimpl_->application_.get());
495 // LyX::pimpl_::application_ is not initialised at this
496 // point so it's safe to just exit after some cleanup.
502 int LyX::init(int & argc, char * argv[])
504 // check for any spurious extra arguments
505 // other than documents
506 for (int argi = 1; argi < argc ; ++argi) {
507 if (argv[argi][0] == '-') {
509 bformat(_("Wrong command line option `%1$s'. Exiting."),
510 from_utf8(argv[argi]))) << endl;
515 // Initialization of LyX (reads lyxrc and more)
516 LYXERR(Debug::INIT, "Initializing LyX::init...");
517 bool success = init();
518 LYXERR(Debug::INIT, "Initializing LyX::init...done");
522 for (int argi = argc - 1; argi >= 1; --argi) {
523 // get absolute path of file and add ".lyx" to
524 // the filename if necessary
525 pimpl_->files_to_load_.push_back(fileSearch(string(),
526 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
527 "lyx", support::allow_unreadable));
531 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
537 void LyX::addFileToLoad(FileName const & fname)
539 vector<FileName>::const_iterator cit = std::find(
540 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
543 if (cit == pimpl_->files_to_load_.end())
544 pimpl_->files_to_load_.push_back(fname);
548 void LyX::loadFiles()
550 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
551 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
553 for (; it != end; ++it) {
557 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
558 if (buf->loadLyXFile(*it)) {
559 ErrorList const & el = buf->errorList("Parse");
561 for_each(el.begin(), el.end(),
562 boost::bind(&LyX::printError, this, _1));
565 pimpl_->buffer_list_.release(buf);
570 void LyX::execBatchCommands()
572 // The advantage of doing this here is that the event loop
573 // is already started. So any need for interaction will be
577 // if reconfiguration is needed.
578 if (textclasslist.empty()) {
579 switch (Alert::prompt(
580 _("No textclass is found"),
581 _("LyX cannot continue because no textclass is found. "
582 "You can either reconfigure normally, or reconfigure using "
583 "default textclasses, or quit LyX."),
590 // regular reconfigure
591 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
594 // reconfigure --without-latex-config
595 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
596 " --without-latex-config"));
599 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
603 // Execute batch commands if available
604 if (pimpl_->batch_command.empty())
607 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
609 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
613 void LyX::restoreGuiSession()
615 // create the main window
616 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
618 // if there is no valid class list, do not load any file.
619 if (textclasslist.empty())
622 // if some files were specified at command-line we assume that the
623 // user wants to edit *these* files and not to restore the session.
624 if (!pimpl_->files_to_load_.empty()) {
625 for_each(pimpl_->files_to_load_.begin(),
626 pimpl_->files_to_load_.end(),
627 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, true));
628 // clear this list to save a few bytes of RAM
629 pimpl_->files_to_load_.clear();
630 pimpl_->session_->lastOpened().clear();
632 } else if (lyxrc.load_session) {
633 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
634 // do not add to the lastfile list since these files are restored from
635 // last session, and should be already there (regular files), or should
636 // not be added at all (help files).
637 for_each(lastopened.begin(), lastopened.end(),
638 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, false));
640 // clear this list to save a few bytes of RAM
641 pimpl_->session_->lastOpened().clear();
644 BufferList::iterator I = pimpl_->buffer_list_.begin();
645 BufferList::iterator end = pimpl_->buffer_list_.end();
646 for (; I != end; ++I) {
648 if (buf != buf->masterBuffer())
657 The SIGHUP signal does not exist on Windows and does not need to be handled.
659 Windows handles SIGFPE and SIGSEGV signals as expected.
661 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
662 cause a new thread to be spawned. This may well result in unexpected
663 behaviour by the single-threaded LyX.
665 SIGTERM signals will come only from another process actually sending
666 that signal using 'raise' in Windows' POSIX compatability layer. It will
667 not come from the general "terminate process" methods that everyone
668 actually uses (and which can't be trapped). Killing an app 'politely' on
669 Windows involves first sending a WM_CLOSE message, something that is
670 caught already by the Qt frontend.
672 For more information see:
674 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
675 ...signals are mostly useless on Windows for a variety of reasons that are
678 'UNIX Application Migration Guide, Chapter 9'
679 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
681 'How To Terminate an Application "Cleanly" in Win32'
682 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
686 static void error_handler(int err_sig)
688 // Throw away any signals other than the first one received.
689 static sig_atomic_t handling_error = false;
692 handling_error = true;
694 // We have received a signal indicating a fatal error, so
695 // try and save the data ASAP.
696 LyX::cref().emergencyCleanup();
698 // These lyxerr calls may or may not work:
700 // Signals are asynchronous, so the main program may be in a very
701 // fragile state when a signal is processed and thus while a signal
702 // handler function executes.
703 // In general, therefore, we should avoid performing any
704 // I/O operations or calling most library and system functions from
707 // This shouldn't matter here, however, as we've already invoked
712 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
716 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
719 lyxerr << "\nlyx: SIGSEGV signal caught\n"
720 "Sorry, you have found a bug in LyX. "
721 "Please read the bug-reporting instructions "
722 "in Help->Introduction and send us a bug report, "
723 "if necessary. Thanks !\nBye." << endl;
731 // Deinstall the signal handlers
733 signal(SIGHUP, SIG_DFL);
735 signal(SIGINT, SIG_DFL);
736 signal(SIGFPE, SIG_DFL);
737 signal(SIGSEGV, SIG_DFL);
738 signal(SIGTERM, SIG_DFL);
741 if (err_sig == SIGSEGV ||
742 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
744 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
753 void LyX::printError(ErrorItem const & ei)
755 docstring tmp = _("LyX: ") + ei.error + char_type(':')
757 std::cerr << to_utf8(tmp) << std::endl;
764 signal(SIGHUP, error_handler);
766 signal(SIGFPE, error_handler);
767 signal(SIGSEGV, error_handler);
768 signal(SIGINT, error_handler);
769 signal(SIGTERM, error_handler);
770 // SIGPIPE can be safely ignored.
772 lyxrc.tempdir_path = package().temp_dir().absFilename();
773 lyxrc.document_path = package().document_dir().absFilename();
775 if (lyxrc.example_path.empty()) {
776 lyxrc.example_path = addPath(package().system_support().absFilename(),
779 if (lyxrc.template_path.empty()) {
780 lyxrc.template_path = addPath(package().system_support().absFilename(),
785 // Read configuration files
788 // This one may have been distributed along with LyX.
789 if (!readRcFile("lyxrc.dist"))
792 // Set the language defined by the distributor.
793 //setGuiLanguage(lyxrc.gui_language);
795 // Set the PATH correctly.
796 #if !defined (USE_POSIX_PACKAGING)
797 // Add the directory containing the LyX executable to the path
798 // so that LyX can find things like tex2lyx.
799 if (package().build_support().empty())
800 prependEnvPath("PATH", package().binary_dir().absFilename());
802 if (!lyxrc.path_prefix.empty())
803 prependEnvPath("PATH", lyxrc.path_prefix);
805 // Check that user LyX directory is ok.
806 if (queryUserLyXDir(package().explicit_user_support()))
807 reconfigureUserLyXDir();
809 // no need for a splash when there is no GUI
814 // This one is generated in user_support directory by lib/configure.py.
815 if (!readRcFile("lyxrc.defaults"))
818 // Query the OS to know what formats are viewed natively
819 formats.setAutoOpen();
821 // Read lyxrc.dist again to be able to override viewer auto-detection.
822 readRcFile("lyxrc.dist");
824 system_lyxrc = lyxrc;
825 system_formats = formats;
826 pimpl_->system_converters_ = pimpl_->converters_;
827 pimpl_->system_movers_ = pimpl_->movers_;
828 system_lcolor = lcolor;
830 // This one is edited through the preferences dialog.
831 if (!readRcFile("preferences"))
834 if (!readEncodingsFile("encodings", "unicodesymbols"))
836 if (!readLanguagesFile("languages"))
840 LYXERR(Debug::INIT, "Reading layouts...");
846 // read keymap and ui files in batch mode as well
847 // because InsetInfo needs to know these to produce
848 // the correct output
850 // Set the language defined by the user.
851 //setGuiLanguage(lyxrc.gui_language);
853 // Set up command definitions
854 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
857 pimpl_->toplevel_keymap_.read("site");
858 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
859 // load user bind file user.bind
860 pimpl_->toplevel_keymap_.read("user");
862 pimpl_->lyxfunc_.initKeySequences(&pimpl_->toplevel_keymap_);
865 if (!readUIFile(lyxrc.ui_file))
868 if (lyxerr.debugging(Debug::LYXRC))
871 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
872 if (!lyxrc.path_prefix.empty())
873 prependEnvPath("PATH", lyxrc.path_prefix);
875 FileName const document_path(lyxrc.document_path);
876 if (document_path.exists() && document_path.isDirectory())
877 package().document_dir() = document_path;
879 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
880 if (package().temp_dir().empty()) {
881 Alert::error(_("Could not create temporary directory"),
882 bformat(_("Could not create a temporary directory in\n"
883 "%1$s. Make sure that this\n"
884 "path exists and is writable and try again."),
885 from_utf8(lyxrc.tempdir_path)));
886 // createLyXTmpDir() tries sufficiently hard to create a
887 // usable temp dir, so the probability to come here is
888 // close to zero. We therefore don't try to overcome this
889 // problem with e.g. asking the user for a new path and
890 // trying again but simply exit.
894 LYXERR(Debug::INIT, "LyX tmp dir: `"
895 << package().temp_dir().absFilename() << '\'');
897 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
898 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
900 // This must happen after package initialization and after lyxrc is
901 // read, therefore it can't be done by a static object.
902 ConverterCache::init();
908 void LyX::emergencyCleanup() const
910 // what to do about tmpfiles is non-obvious. we would
911 // like to delete any we find, but our lyxdir might
912 // contain documents etc. which might be helpful on
915 pimpl_->buffer_list_.emergencyWriteAll();
917 if (pimpl_->lyx_server_)
918 pimpl_->lyx_server_->emergencyCleanup();
919 pimpl_->lyx_server_.reset();
920 pimpl_->lyx_socket_.reset();
925 void LyX::deadKeyBindings(KeyMap * kbmap)
927 // bindKeyings for transparent handling of deadkeys
928 // The keysyms are gotten from XFree86 X11R6
929 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
930 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
931 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
932 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
933 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
934 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
935 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
936 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
937 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
938 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
939 // nothing with this name
940 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
941 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
942 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
943 // nothing with this name either...
944 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
945 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
946 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
947 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
951 // return true if file does not exist or is older than configure.py.
952 static bool needsUpdate(string const & file)
954 // We cannot initialize configure_script directly because the package
955 // is not initialized yet when static objects are constructed.
956 static FileName configure_script;
957 static bool firstrun = true;
960 FileName(addName(package().system_support().absFilename(),
966 FileName(addName(package().user_support().absFilename(), file));
967 return !absfile.exists()
968 || configure_script.lastModified() > absfile.lastModified();
972 bool LyX::queryUserLyXDir(bool explicit_userdir)
974 // Does user directory exist?
975 FileName const sup = package().user_support();
976 if (sup.exists() && sup.isDirectory()) {
979 return needsUpdate("lyxrc.defaults")
980 || needsUpdate("lyxmodules.lst")
981 || needsUpdate("textclass.lst")
982 || needsUpdate("packages.lst");
985 first_start = !explicit_userdir;
987 // If the user specified explicitly a directory, ask whether
988 // to create it. If the user says "no", then exit.
989 if (explicit_userdir &&
991 _("Missing user LyX directory"),
992 bformat(_("You have specified a non-existent user "
993 "LyX directory, %1$s.\n"
994 "It is needed to keep your own configuration."),
995 from_utf8(package().user_support().absFilename())),
997 _("&Create directory"),
999 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1000 earlyExit(EXIT_FAILURE);
1003 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1004 from_utf8(sup.absFilename()))) << endl;
1006 if (!sup.createDirectory(0755)) {
1007 // Failed, so let's exit.
1008 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1010 earlyExit(EXIT_FAILURE);
1017 bool LyX::readRcFile(string const & name)
1019 LYXERR(Debug::INIT, "About to read " << name << "... ");
1021 FileName const lyxrc_path = libFileSearch(string(), name);
1022 if (!lyxrc_path.empty()) {
1023 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1024 if (lyxrc.read(lyxrc_path) < 0) {
1025 showFileError(name);
1029 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1035 // Read the ui file `name'
1036 bool LyX::readUIFile(string const & name, bool include)
1046 struct keyword_item uitags[ui_last - 1] = {
1047 { "include", ui_include },
1048 { "menuset", ui_menuset },
1049 { "toolbars", ui_toolbars },
1050 { "toolbarset", ui_toolbarset }
1053 // Ensure that a file is read only once (prevents include loops)
1054 static std::list<string> uifiles;
1055 std::list<string>::const_iterator it = uifiles.begin();
1056 std::list<string>::const_iterator end = uifiles.end();
1057 it = std::find(it, end, name);
1059 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1060 << "Is this an include loop?");
1064 LYXERR(Debug::INIT, "About to read " << name << "...");
1069 ui_path = libFileSearch("ui", name, "inc");
1070 if (ui_path.empty())
1071 ui_path = libFileSearch("ui",
1072 changeExtension(name, "inc"));
1075 ui_path = libFileSearch("ui", name, "ui");
1077 if (ui_path.empty()) {
1078 LYXERR(Debug::INIT, "Could not find " << name);
1079 showFileError(name);
1083 uifiles.push_back(name);
1085 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1086 Lexer lex(uitags, ui_last - 1);
1087 lex.setFile(ui_path);
1089 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1093 if (lyxerr.debugging(Debug::PARSER))
1094 lex.printTable(lyxerr);
1096 while (lex.isOK()) {
1097 switch (lex.lex()) {
1100 string const file = lex.getString();
1101 if (!readUIFile(file, true))
1106 menubackend.read(lex);
1110 toolbarbackend.readToolbars(lex);
1114 toolbarbackend.readToolbarSettings(lex);
1118 if (!rtrim(lex.getString()).empty())
1119 lex.printError("LyX::ReadUIFile: "
1120 "Unknown menu tag: `$$Token'");
1128 // Read the languages file `name'
1129 bool LyX::readLanguagesFile(string const & name)
1131 LYXERR(Debug::INIT, "About to read " << name << "...");
1133 FileName const lang_path = libFileSearch(string(), name);
1134 if (lang_path.empty()) {
1135 showFileError(name);
1138 languages.read(lang_path);
1143 // Read the encodings file `name'
1144 bool LyX::readEncodingsFile(string const & enc_name,
1145 string const & symbols_name)
1147 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1148 << symbols_name << "...");
1150 FileName const symbols_path = libFileSearch(string(), symbols_name);
1151 if (symbols_path.empty()) {
1152 showFileError(symbols_name);
1156 FileName const enc_path = libFileSearch(string(), enc_name);
1157 if (enc_path.empty()) {
1158 showFileError(enc_name);
1161 encodings.read(enc_path, symbols_path);
1170 /// return the the number of arguments consumed
1171 typedef boost::function<int(string const &, string const &)> cmd_helper;
1173 int parse_dbg(string const & arg, string const &)
1176 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1177 Debug::showTags(lyxerr);
1180 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1182 lyxerr.level(Debug::value(arg));
1183 Debug::showLevel(lyxerr, lyxerr.level());
1188 int parse_help(string const &, string const &)
1191 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1192 "Command line switches (case sensitive):\n"
1193 "\t-help summarize LyX usage\n"
1194 "\t-userdir dir set user directory to dir\n"
1195 "\t-sysdir dir set system directory to dir\n"
1196 "\t-geometry WxH+X+Y set geometry of the main window\n"
1197 "\t-dbg feature[,feature]...\n"
1198 " select the features to debug.\n"
1199 " Type `lyx -dbg' to see the list of features\n"
1200 "\t-x [--execute] command\n"
1201 " where command is a lyx command.\n"
1202 "\t-e [--export] fmt\n"
1203 " where fmt is the export format of choice.\n"
1204 " Look on Tools->Preferences->File formats->Format\n"
1205 " to get an idea which parameters should be passed.\n"
1206 "\t-i [--import] fmt file.xxx\n"
1207 " where fmt is the import format of choice\n"
1208 " and file.xxx is the file to be imported.\n"
1209 "\t-version summarize version and build info\n"
1210 "Check the LyX man page for more details.")) << endl;
1216 int parse_version(string const &, string const &)
1218 lyxerr << "LyX " << lyx_version
1219 << " (" << lyx_release_date << ")" << endl;
1220 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1222 lyxerr << lyx_version_info << endl;
1228 int parse_sysdir(string const & arg, string const &)
1231 Alert::error(_("No system directory"),
1232 _("Missing directory for -sysdir switch"));
1235 cl_system_support = arg;
1240 int parse_userdir(string const & arg, string const &)
1243 Alert::error(_("No user directory"),
1244 _("Missing directory for -userdir switch"));
1247 cl_user_support = arg;
1252 int parse_execute(string const & arg, string const &)
1255 Alert::error(_("Incomplete command"),
1256 _("Missing command string after --execute switch"));
1264 int parse_export(string const & type, string const &)
1267 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1268 "--export switch")) << endl;
1271 batch = "buffer-export " + type;
1277 int parse_import(string const & type, string const & file)
1280 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1281 "--import switch")) << endl;
1285 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1289 batch = "buffer-import " + type + ' ' + file;
1294 int parse_geometry(string const & arg1, string const &)
1297 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1298 // remove also the arg
1301 // don't remove "-geometry"
1310 void LyX::easyParse(int & argc, char * argv[])
1312 std::map<string, cmd_helper> cmdmap;
1314 cmdmap["-dbg"] = parse_dbg;
1315 cmdmap["-help"] = parse_help;
1316 cmdmap["--help"] = parse_help;
1317 cmdmap["-version"] = parse_version;
1318 cmdmap["--version"] = parse_version;
1319 cmdmap["-sysdir"] = parse_sysdir;
1320 cmdmap["-userdir"] = parse_userdir;
1321 cmdmap["-x"] = parse_execute;
1322 cmdmap["--execute"] = parse_execute;
1323 cmdmap["-e"] = parse_export;
1324 cmdmap["--export"] = parse_export;
1325 cmdmap["-i"] = parse_import;
1326 cmdmap["--import"] = parse_import;
1327 cmdmap["-geometry"] = parse_geometry;
1329 for (int i = 1; i < argc; ++i) {
1330 std::map<string, cmd_helper>::const_iterator it
1331 = cmdmap.find(argv[i]);
1333 // don't complain if not found - may be parsed later
1334 if (it == cmdmap.end())
1338 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1340 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1342 int const remove = 1 + it->second(arg, arg2);
1344 // Now, remove used arguments by shifting
1345 // the following ones remove places down.
1348 for (int j = i; j < argc; ++j)
1349 argv[j] = argv[j + remove];
1354 pimpl_->batch_command = batch;
1358 FuncStatus getStatus(FuncRequest const & action)
1360 return LyX::ref().lyxFunc().getStatus(action);
1364 void dispatch(FuncRequest const & action)
1366 LyX::ref().lyxFunc().dispatch(action);
1370 BufferList & theBufferList()
1372 return LyX::ref().bufferList();
1376 LyXFunc & theLyXFunc()
1378 return LyX::ref().lyxFunc();
1382 Server & theServer()
1384 // FIXME: this should not be use_gui dependent
1385 BOOST_ASSERT(use_gui);
1386 return LyX::ref().server();
1390 ServerSocket & theServerSocket()
1392 // FIXME: this should not be use_gui dependent
1393 BOOST_ASSERT(use_gui);
1394 return LyX::ref().socket();
1398 KeyMap & theTopLevelKeymap()
1400 return LyX::ref().topLevelKeymap();
1404 Converters & theConverters()
1406 return LyX::ref().converters();
1410 Converters & theSystemConverters()
1412 return LyX::ref().systemConverters();
1416 Movers & theMovers()
1418 return LyX::ref().pimpl_->movers_;
1422 Mover const & getMover(std::string const & fmt)
1424 return LyX::ref().pimpl_->movers_(fmt);
1428 void setMover(std::string const & fmt, std::string const & command)
1430 LyX::ref().pimpl_->movers_.set(fmt, command);
1434 Movers & theSystemMovers()
1436 return LyX::ref().pimpl_->system_movers_;
1440 Messages & getMessages(std::string const & language)
1442 return LyX::ref().getMessages(language);
1446 Messages & getGuiMessages()
1448 return LyX::ref().getGuiMessages();