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>
79 #ifndef CXX_GLOBAL_CSTD
87 using support::addName;
88 using support::addPath;
89 using support::bformat;
90 using support::changeExtension;
91 using support::createLyXTmpDir;
92 using support::FileName;
93 using support::fileSearch;
94 using support::getEnv;
95 using support::i18nLibFileSearch;
96 using support::libFileSearch;
97 using support::package;
98 using support::prependEnvPath;
100 using support::Systemcall;
102 namespace Alert = frontend::Alert;
103 namespace os = support::os;
107 // Are we using the GUI at all? We default to true and this is changed
108 // to false when the export feature is used.
112 bool quitting; // flag, that we are quitting the program
116 // Filled with the command line arguments "foo" of "-sysdir foo" or
118 string cl_system_support;
119 string cl_user_support;
121 std::string geometryArg;
123 LyX * singleton_ = 0;
125 void showFileError(string const & error)
127 Alert::warning(_("Could not read configuration file"),
128 bformat(_("Error while reading the configuration file\n%1$s.\n"
129 "Please check your installation."), from_utf8(error)));
133 void reconfigureUserLyXDir()
135 string const configure_command = package().configure_command();
137 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
138 support::PathChanger p(package().user_support());
140 one.startscript(Systemcall::Wait, configure_command);
141 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
147 /// The main application class private implementation.
152 // Set the default User Interface language as soon as possible.
153 // The language used will be derived from the environment
155 messages_["GUI"] = Messages();
157 /// our function handler
160 BufferList buffer_list_;
162 KeyMap toplevel_keymap_;
164 CmdDef toplevel_cmddef_;
166 boost::scoped_ptr<Server> lyx_server_;
168 boost::scoped_ptr<ServerSocket> lyx_socket_;
170 boost::scoped_ptr<frontend::Application> application_;
171 /// lyx session, containing lastfiles, lastfilepos, and lastopened
172 boost::scoped_ptr<Session> session_;
174 /// Files to load at start.
175 vector<FileName> files_to_load_;
177 /// The messages translators.
178 map<string, Messages> messages_;
180 /// The file converters.
181 Converters converters_;
183 // The system converters copy after reading lyxrc.defaults.
184 Converters system_converters_;
189 Movers system_movers_;
191 /// has this user started lyx for the first time?
193 /// the parsed command line batch command if any
194 std::string batch_command;
198 frontend::Application * theApp()
201 return singleton_->pimpl_->application_.get();
215 BOOST_ASSERT(singleton_);
220 LyX const & LyX::cref()
222 BOOST_ASSERT(singleton_);
235 BufferList & LyX::bufferList()
237 return pimpl_->buffer_list_;
241 BufferList const & LyX::bufferList() const
243 return pimpl_->buffer_list_;
247 Session & LyX::session()
249 BOOST_ASSERT(pimpl_->session_.get());
250 return *pimpl_->session_.get();
254 Session const & LyX::session() const
256 BOOST_ASSERT(pimpl_->session_.get());
257 return *pimpl_->session_.get();
261 LyXFunc & LyX::lyxFunc()
263 return pimpl_->lyxfunc_;
267 LyXFunc const & LyX::lyxFunc() const
269 return pimpl_->lyxfunc_;
273 Server & LyX::server()
275 BOOST_ASSERT(pimpl_->lyx_server_.get());
276 return *pimpl_->lyx_server_.get();
280 Server const & LyX::server() const
282 BOOST_ASSERT(pimpl_->lyx_server_.get());
283 return *pimpl_->lyx_server_.get();
287 ServerSocket & LyX::socket()
289 BOOST_ASSERT(pimpl_->lyx_socket_.get());
290 return *pimpl_->lyx_socket_.get();
294 ServerSocket const & LyX::socket() const
296 BOOST_ASSERT(pimpl_->lyx_socket_.get());
297 return *pimpl_->lyx_socket_.get();
301 frontend::Application & LyX::application()
303 BOOST_ASSERT(pimpl_->application_.get());
304 return *pimpl_->application_.get();
308 frontend::Application const & LyX::application() const
310 BOOST_ASSERT(pimpl_->application_.get());
311 return *pimpl_->application_.get();
315 KeyMap & LyX::topLevelKeymap()
317 return pimpl_->toplevel_keymap_;
321 CmdDef & LyX::topLevelCmdDef()
323 return pimpl_->toplevel_cmddef_;
327 Converters & LyX::converters()
329 return pimpl_->converters_;
333 Converters & LyX::systemConverters()
335 return pimpl_->system_converters_;
339 KeyMap const & LyX::topLevelKeymap() const
341 return pimpl_->toplevel_keymap_;
345 Messages & LyX::getMessages(std::string const & language)
347 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
349 if (it != pimpl_->messages_.end())
352 std::pair<map<string, Messages>::iterator, bool> result =
353 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
355 BOOST_ASSERT(result.second);
356 return result.first->second;
360 Messages & LyX::getGuiMessages()
362 return pimpl_->messages_["GUI"];
366 void LyX::setGuiLanguage(std::string const & language)
368 pimpl_->messages_["GUI"] = Messages(language);
372 int LyX::exec(int & argc, char * argv[])
374 // Here we need to parse the command line. At least
375 // we need to parse for "-dbg" and "-help"
376 easyParse(argc, argv);
379 support::init_package(to_utf8(from_local8bit(argv[0])),
380 cl_system_support, cl_user_support,
381 support::top_build_dir_is_one_level_up);
382 } catch (support::ExceptionMessage const & message) {
383 if (message.type_ == support::ErrorException) {
384 Alert::error(message.title_, message.details_);
386 } else if (message.type_ == support::WarningException) {
387 Alert::warning(message.title_, message.details_);
391 // Reinit the messages machinery in case package() knows
392 // something interesting about the locale directory.
396 // FIXME: create a ConsoleApplication
397 int exit_status = init(argc, argv);
405 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
410 BufferList::iterator begin = pimpl_->buffer_list_.begin();
412 bool final_success = false;
413 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
415 if (buf != buf->masterBuffer())
417 bool success = false;
418 buf->dispatch(pimpl_->batch_command, &success);
419 final_success |= success;
422 return !final_success;
425 // Let the frontend parse and remove all arguments that it knows
426 pimpl_->application_.reset(createApplication(argc, argv));
428 // Parse and remove all known arguments in the LyX singleton
429 // Give an error for all remaining ones.
430 int exit_status = init(argc, argv);
432 // Kill the application object before exiting.
433 pimpl_->application_.reset();
440 /* Create a CoreApplication class that will provide the main event loop
441 * and the socket callback registering. With Qt4, only QtCore
442 * library would be needed.
443 * When this is done, a server_mode could be created and the following two
444 * line would be moved out from here.
446 // Note: socket callback must be registered after init(argc, argv)
447 // such that package().temp_dir() is properly initialized.
448 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
449 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
450 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
452 // Start the real execution loop.
453 exit_status = pimpl_->application_->exec();
461 void LyX::prepareExit()
463 // Clear the clipboard and selection stack:
464 cap::clearCutStack();
465 cap::clearSelection();
467 // Set a flag that we do quitting from the program,
468 // so no refreshes are necessary.
471 // close buffers first
472 pimpl_->buffer_list_.closeAll();
474 // do any other cleanup procedures now
475 if (package().temp_dir() != package().system_temp_dir()) {
476 LYXERR(Debug::INFO, "Deleting tmp dir "
477 << package().temp_dir().absFilename());
479 if (!package().temp_dir().destroyDirectory()) {
480 docstring const msg =
481 bformat(_("Unable to remove the temporary directory %1$s"),
482 from_utf8(package().temp_dir().absFilename()));
483 Alert::warning(_("Unable to remove temporary directory"), msg);
488 if (pimpl_->session_)
489 pimpl_->session_->writeFile();
490 pimpl_->session_.reset();
491 pimpl_->lyx_server_.reset();
492 pimpl_->lyx_socket_.reset();
495 // Kill the application object before exiting. This avoids crashes
496 // when exiting on Linux.
497 if (pimpl_->application_)
498 pimpl_->application_.reset();
502 void LyX::earlyExit(int status)
504 BOOST_ASSERT(pimpl_->application_.get());
505 // LyX::pimpl_::application_ is not initialised at this
506 // point so it's safe to just exit after some cleanup.
512 int LyX::init(int & argc, char * argv[])
514 // check for any spurious extra arguments
515 // other than documents
516 for (int argi = 1; argi < argc ; ++argi) {
517 if (argv[argi][0] == '-') {
519 bformat(_("Wrong command line option `%1$s'. Exiting."),
520 from_utf8(argv[argi]))) << endl;
525 // Initialization of LyX (reads lyxrc and more)
526 LYXERR(Debug::INIT, "Initializing LyX::init...");
527 bool success = init();
528 LYXERR(Debug::INIT, "Initializing LyX::init...done");
532 for (int argi = argc - 1; argi >= 1; --argi) {
533 // get absolute path of file and add ".lyx" to
534 // the filename if necessary
535 pimpl_->files_to_load_.push_back(fileSearch(string(),
536 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
537 "lyx", support::allow_unreadable));
541 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
547 void LyX::addFileToLoad(FileName const & fname)
549 vector<FileName>::const_iterator cit = std::find(
550 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
553 if (cit == pimpl_->files_to_load_.end())
554 pimpl_->files_to_load_.push_back(fname);
558 void LyX::loadFiles()
560 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
561 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
563 for (; it != end; ++it) {
567 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
568 if (buf->loadLyXFile(*it)) {
569 ErrorList const & el = buf->errorList("Parse");
571 for_each(el.begin(), el.end(),
572 boost::bind(&LyX::printError, this, _1));
575 pimpl_->buffer_list_.release(buf);
580 void LyX::execBatchCommands()
582 // The advantage of doing this here is that the event loop
583 // is already started. So any need for interaction will be
587 // if reconfiguration is needed.
588 if (textclasslist.empty()) {
589 switch (Alert::prompt(
590 _("No textclass is found"),
591 _("LyX cannot continue because no textclass is found. "
592 "You can either reconfigure normally, or reconfigure using "
593 "default textclasses, or quit LyX."),
600 // regular reconfigure
601 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
604 // reconfigure --without-latex-config
605 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
606 " --without-latex-config"));
609 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
613 // Execute batch commands if available
614 if (pimpl_->batch_command.empty())
617 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
619 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
623 void LyX::restoreGuiSession()
625 // create the main window
626 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
628 // if there is no valid class list, do not load any file.
629 if (textclasslist.empty())
632 // if some files were specified at command-line we assume that the
633 // user wants to edit *these* files and not to restore the session.
634 if (!pimpl_->files_to_load_.empty()) {
635 for_each(pimpl_->files_to_load_.begin(),
636 pimpl_->files_to_load_.end(),
637 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, true));
638 // clear this list to save a few bytes of RAM
639 pimpl_->files_to_load_.clear();
640 pimpl_->session_->lastOpened().clear();
642 } else if (lyxrc.load_session) {
643 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
644 // do not add to the lastfile list since these files are restored from
645 // last session, and should be already there (regular files), or should
646 // not be added at all (help files).
647 for_each(lastopened.begin(), lastopened.end(),
648 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, false));
650 // clear this list to save a few bytes of RAM
651 pimpl_->session_->lastOpened().clear();
654 BufferList::iterator I = pimpl_->buffer_list_.begin();
655 BufferList::iterator end = pimpl_->buffer_list_.end();
656 for (; I != end; ++I) {
658 if (buf != buf->masterBuffer())
667 The SIGHUP signal does not exist on Windows and does not need to be handled.
669 Windows handles SIGFPE and SIGSEGV signals as expected.
671 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
672 cause a new thread to be spawned. This may well result in unexpected
673 behaviour by the single-threaded LyX.
675 SIGTERM signals will come only from another process actually sending
676 that signal using 'raise' in Windows' POSIX compatability layer. It will
677 not come from the general "terminate process" methods that everyone
678 actually uses (and which can't be trapped). Killing an app 'politely' on
679 Windows involves first sending a WM_CLOSE message, something that is
680 caught already by the Qt frontend.
682 For more information see:
684 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
685 ...signals are mostly useless on Windows for a variety of reasons that are
688 'UNIX Application Migration Guide, Chapter 9'
689 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
691 'How To Terminate an Application "Cleanly" in Win32'
692 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
696 static void error_handler(int err_sig)
698 // Throw away any signals other than the first one received.
699 static sig_atomic_t handling_error = false;
702 handling_error = true;
704 // We have received a signal indicating a fatal error, so
705 // try and save the data ASAP.
706 LyX::cref().emergencyCleanup();
708 // These lyxerr calls may or may not work:
710 // Signals are asynchronous, so the main program may be in a very
711 // fragile state when a signal is processed and thus while a signal
712 // handler function executes.
713 // In general, therefore, we should avoid performing any
714 // I/O operations or calling most library and system functions from
717 // This shouldn't matter here, however, as we've already invoked
722 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
726 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
729 lyxerr << "\nlyx: SIGSEGV signal caught\n"
730 "Sorry, you have found a bug in LyX. "
731 "Please read the bug-reporting instructions "
732 "in Help->Introduction and send us a bug report, "
733 "if necessary. Thanks !\nBye." << endl;
741 // Deinstall the signal handlers
743 signal(SIGHUP, SIG_DFL);
745 signal(SIGINT, SIG_DFL);
746 signal(SIGFPE, SIG_DFL);
747 signal(SIGSEGV, SIG_DFL);
748 signal(SIGTERM, SIG_DFL);
751 if (err_sig == SIGSEGV ||
752 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
754 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
763 void LyX::printError(ErrorItem const & ei)
765 docstring tmp = _("LyX: ") + ei.error + char_type(':')
767 std::cerr << to_utf8(tmp) << std::endl;
774 signal(SIGHUP, error_handler);
776 signal(SIGFPE, error_handler);
777 signal(SIGSEGV, error_handler);
778 signal(SIGINT, error_handler);
779 signal(SIGTERM, error_handler);
780 // SIGPIPE can be safely ignored.
782 lyxrc.tempdir_path = package().temp_dir().absFilename();
783 lyxrc.document_path = package().document_dir().absFilename();
785 if (lyxrc.example_path.empty()) {
786 lyxrc.example_path = addPath(package().system_support().absFilename(),
789 if (lyxrc.template_path.empty()) {
790 lyxrc.template_path = addPath(package().system_support().absFilename(),
795 // Read configuration files
798 // This one may have been distributed along with LyX.
799 if (!readRcFile("lyxrc.dist"))
802 // Set the language defined by the distributor.
803 //setGuiLanguage(lyxrc.gui_language);
805 // Set the PATH correctly.
806 #if !defined (USE_POSIX_PACKAGING)
807 // Add the directory containing the LyX executable to the path
808 // so that LyX can find things like tex2lyx.
809 if (package().build_support().empty())
810 prependEnvPath("PATH", package().binary_dir().absFilename());
812 if (!lyxrc.path_prefix.empty())
813 prependEnvPath("PATH", lyxrc.path_prefix);
815 // Check that user LyX directory is ok.
816 if (queryUserLyXDir(package().explicit_user_support()))
817 reconfigureUserLyXDir();
819 // no need for a splash when there is no GUI
824 // This one is generated in user_support directory by lib/configure.py.
825 if (!readRcFile("lyxrc.defaults"))
828 // Query the OS to know what formats are viewed natively
829 formats.setAutoOpen();
831 // Read lyxrc.dist again to be able to override viewer auto-detection.
832 readRcFile("lyxrc.dist");
834 system_lyxrc = lyxrc;
835 system_formats = formats;
836 pimpl_->system_converters_ = pimpl_->converters_;
837 pimpl_->system_movers_ = pimpl_->movers_;
838 system_lcolor = lcolor;
840 // This one is edited through the preferences dialog.
841 if (!readRcFile("preferences"))
844 if (!readEncodingsFile("encodings", "unicodesymbols"))
846 if (!readLanguagesFile("languages"))
850 LYXERR(Debug::INIT, "Reading layouts...");
856 // read keymap and ui files in batch mode as well
857 // because InsetInfo needs to know these to produce
858 // the correct output
860 // Set the language defined by the user.
861 //setGuiLanguage(lyxrc.gui_language);
863 // Set up command definitions
864 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
867 pimpl_->toplevel_keymap_.read("site");
868 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
869 // load user bind file user.bind
870 pimpl_->toplevel_keymap_.read("user");
872 pimpl_->lyxfunc_.initKeySequences(&pimpl_->toplevel_keymap_);
875 if (!readUIFile(lyxrc.ui_file))
878 if (lyxerr.debugging(Debug::LYXRC))
881 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
882 if (!lyxrc.path_prefix.empty())
883 prependEnvPath("PATH", lyxrc.path_prefix);
885 FileName const document_path(lyxrc.document_path);
886 if (document_path.exists() && document_path.isDirectory())
887 package().document_dir() = document_path;
889 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
890 if (package().temp_dir().empty()) {
891 Alert::error(_("Could not create temporary directory"),
892 bformat(_("Could not create a temporary directory in\n"
893 "%1$s. Make sure that this\n"
894 "path exists and is writable and try again."),
895 from_utf8(lyxrc.tempdir_path)));
896 // createLyXTmpDir() tries sufficiently hard to create a
897 // usable temp dir, so the probability to come here is
898 // close to zero. We therefore don't try to overcome this
899 // problem with e.g. asking the user for a new path and
900 // trying again but simply exit.
904 LYXERR(Debug::INIT, "LyX tmp dir: `"
905 << package().temp_dir().absFilename() << '\'');
907 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
908 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
910 // This must happen after package initialization and after lyxrc is
911 // read, therefore it can't be done by a static object.
912 ConverterCache::init();
918 void LyX::emergencyCleanup() const
920 // what to do about tmpfiles is non-obvious. we would
921 // like to delete any we find, but our lyxdir might
922 // contain documents etc. which might be helpful on
925 pimpl_->buffer_list_.emergencyWriteAll();
927 if (pimpl_->lyx_server_)
928 pimpl_->lyx_server_->emergencyCleanup();
929 pimpl_->lyx_server_.reset();
930 pimpl_->lyx_socket_.reset();
935 void LyX::deadKeyBindings(KeyMap * kbmap)
937 // bindKeyings for transparent handling of deadkeys
938 // The keysyms are gotten from XFree86 X11R6
939 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
940 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
941 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
942 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
943 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
944 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
945 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
946 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
947 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
948 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
949 // nothing with this name
950 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
951 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
952 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
953 // nothing with this name either...
954 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
955 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
956 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
957 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
961 // return true if file does not exist or is older than configure.py.
962 static bool needsUpdate(string const & file)
964 // We cannot initialize configure_script directly because the package
965 // is not initialized yet when static objects are constructed.
966 static FileName configure_script;
967 static bool firstrun = true;
970 FileName(addName(package().system_support().absFilename(),
976 FileName(addName(package().user_support().absFilename(), file));
977 return !absfile.exists()
978 || configure_script.lastModified() > absfile.lastModified();
982 bool LyX::queryUserLyXDir(bool explicit_userdir)
984 // Does user directory exist?
985 FileName const sup = package().user_support();
986 if (sup.exists() && sup.isDirectory()) {
989 return needsUpdate("lyxrc.defaults")
990 || needsUpdate("lyxmodules.lst")
991 || needsUpdate("textclass.lst")
992 || needsUpdate("packages.lst");
995 first_start = !explicit_userdir;
997 // If the user specified explicitly a directory, ask whether
998 // to create it. If the user says "no", then exit.
999 if (explicit_userdir &&
1001 _("Missing user LyX directory"),
1002 bformat(_("You have specified a non-existent user "
1003 "LyX directory, %1$s.\n"
1004 "It is needed to keep your own configuration."),
1005 from_utf8(package().user_support().absFilename())),
1007 _("&Create directory"),
1009 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1010 earlyExit(EXIT_FAILURE);
1013 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1014 from_utf8(sup.absFilename()))) << endl;
1016 if (!sup.createDirectory(0755)) {
1017 // Failed, so let's exit.
1018 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1020 earlyExit(EXIT_FAILURE);
1027 bool LyX::readRcFile(string const & name)
1029 LYXERR(Debug::INIT, "About to read " << name << "... ");
1031 FileName const lyxrc_path = libFileSearch(string(), name);
1032 if (!lyxrc_path.empty()) {
1033 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1034 if (lyxrc.read(lyxrc_path) < 0) {
1035 showFileError(name);
1039 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1045 // Read the ui file `name'
1046 bool LyX::readUIFile(string const & name, bool include)
1056 struct keyword_item uitags[ui_last - 1] = {
1057 { "include", ui_include },
1058 { "menuset", ui_menuset },
1059 { "toolbars", ui_toolbars },
1060 { "toolbarset", ui_toolbarset }
1063 // Ensure that a file is read only once (prevents include loops)
1064 static std::list<string> uifiles;
1065 std::list<string>::const_iterator it = uifiles.begin();
1066 std::list<string>::const_iterator end = uifiles.end();
1067 it = std::find(it, end, name);
1069 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1070 << "Is this an include loop?");
1074 LYXERR(Debug::INIT, "About to read " << name << "...");
1079 ui_path = libFileSearch("ui", name, "inc");
1080 if (ui_path.empty())
1081 ui_path = libFileSearch("ui",
1082 changeExtension(name, "inc"));
1085 ui_path = libFileSearch("ui", name, "ui");
1087 if (ui_path.empty()) {
1088 LYXERR(Debug::INIT, "Could not find " << name);
1089 showFileError(name);
1093 uifiles.push_back(name);
1095 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1096 Lexer lex(uitags, ui_last - 1);
1097 lex.setFile(ui_path);
1099 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1103 if (lyxerr.debugging(Debug::PARSER))
1104 lex.printTable(lyxerr);
1106 while (lex.isOK()) {
1107 switch (lex.lex()) {
1110 string const file = lex.getString();
1111 if (!readUIFile(file, true))
1116 menubackend.read(lex);
1120 toolbarbackend.readToolbars(lex);
1124 toolbarbackend.readToolbarSettings(lex);
1128 if (!rtrim(lex.getString()).empty())
1129 lex.printError("LyX::ReadUIFile: "
1130 "Unknown menu tag: `$$Token'");
1138 // Read the languages file `name'
1139 bool LyX::readLanguagesFile(string const & name)
1141 LYXERR(Debug::INIT, "About to read " << name << "...");
1143 FileName const lang_path = libFileSearch(string(), name);
1144 if (lang_path.empty()) {
1145 showFileError(name);
1148 languages.read(lang_path);
1153 // Read the encodings file `name'
1154 bool LyX::readEncodingsFile(string const & enc_name,
1155 string const & symbols_name)
1157 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1158 << symbols_name << "...");
1160 FileName const symbols_path = libFileSearch(string(), symbols_name);
1161 if (symbols_path.empty()) {
1162 showFileError(symbols_name);
1166 FileName const enc_path = libFileSearch(string(), enc_name);
1167 if (enc_path.empty()) {
1168 showFileError(enc_name);
1171 encodings.read(enc_path, symbols_path);
1180 /// return the the number of arguments consumed
1181 typedef boost::function<int(string const &, string const &)> cmd_helper;
1183 int parse_dbg(string const & arg, string const &)
1186 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1187 Debug::showTags(lyxerr);
1190 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1192 lyxerr.level(Debug::value(arg));
1193 Debug::showLevel(lyxerr, lyxerr.level());
1198 int parse_help(string const &, string const &)
1201 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1202 "Command line switches (case sensitive):\n"
1203 "\t-help summarize LyX usage\n"
1204 "\t-userdir dir set user directory to dir\n"
1205 "\t-sysdir dir set system directory to dir\n"
1206 "\t-geometry WxH+X+Y set geometry of the main window\n"
1207 "\t-dbg feature[,feature]...\n"
1208 " select the features to debug.\n"
1209 " Type `lyx -dbg' to see the list of features\n"
1210 "\t-x [--execute] command\n"
1211 " where command is a lyx command.\n"
1212 "\t-e [--export] fmt\n"
1213 " where fmt is the export format of choice.\n"
1214 " Look on Tools->Preferences->File formats->Format\n"
1215 " to get an idea which parameters should be passed.\n"
1216 "\t-i [--import] fmt file.xxx\n"
1217 " where fmt is the import format of choice\n"
1218 " and file.xxx is the file to be imported.\n"
1219 "\t-version summarize version and build info\n"
1220 "Check the LyX man page for more details.")) << endl;
1226 int parse_version(string const &, string const &)
1228 lyxerr << "LyX " << lyx_version
1229 << " (" << lyx_release_date << ")" << endl;
1230 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1232 lyxerr << lyx_version_info << endl;
1238 int parse_sysdir(string const & arg, string const &)
1241 Alert::error(_("No system directory"),
1242 _("Missing directory for -sysdir switch"));
1245 cl_system_support = arg;
1250 int parse_userdir(string const & arg, string const &)
1253 Alert::error(_("No user directory"),
1254 _("Missing directory for -userdir switch"));
1257 cl_user_support = arg;
1262 int parse_execute(string const & arg, string const &)
1265 Alert::error(_("Incomplete command"),
1266 _("Missing command string after --execute switch"));
1274 int parse_export(string const & type, string const &)
1277 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1278 "--export switch")) << endl;
1281 batch = "buffer-export " + type;
1287 int parse_import(string const & type, string const & file)
1290 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1291 "--import switch")) << endl;
1295 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1299 batch = "buffer-import " + type + ' ' + file;
1304 int parse_geometry(string const & arg1, string const &)
1307 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1308 // remove also the arg
1311 // don't remove "-geometry"
1320 void LyX::easyParse(int & argc, char * argv[])
1322 std::map<string, cmd_helper> cmdmap;
1324 cmdmap["-dbg"] = parse_dbg;
1325 cmdmap["-help"] = parse_help;
1326 cmdmap["--help"] = parse_help;
1327 cmdmap["-version"] = parse_version;
1328 cmdmap["--version"] = parse_version;
1329 cmdmap["-sysdir"] = parse_sysdir;
1330 cmdmap["-userdir"] = parse_userdir;
1331 cmdmap["-x"] = parse_execute;
1332 cmdmap["--execute"] = parse_execute;
1333 cmdmap["-e"] = parse_export;
1334 cmdmap["--export"] = parse_export;
1335 cmdmap["-i"] = parse_import;
1336 cmdmap["--import"] = parse_import;
1337 cmdmap["-geometry"] = parse_geometry;
1339 for (int i = 1; i < argc; ++i) {
1340 std::map<string, cmd_helper>::const_iterator it
1341 = cmdmap.find(argv[i]);
1343 // don't complain if not found - may be parsed later
1344 if (it == cmdmap.end())
1348 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1350 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1352 int const remove = 1 + it->second(arg, arg2);
1354 // Now, remove used arguments by shifting
1355 // the following ones remove places down.
1358 for (int j = i; j < argc; ++j)
1359 argv[j] = argv[j + remove];
1364 pimpl_->batch_command = batch;
1368 FuncStatus getStatus(FuncRequest const & action)
1370 return LyX::ref().lyxFunc().getStatus(action);
1374 void dispatch(FuncRequest const & action)
1376 LyX::ref().lyxFunc().dispatch(action);
1380 BufferList & theBufferList()
1382 return LyX::ref().bufferList();
1386 LyXFunc & theLyXFunc()
1388 return LyX::ref().lyxFunc();
1392 Server & theServer()
1394 // FIXME: this should not be use_gui dependent
1395 BOOST_ASSERT(use_gui);
1396 return LyX::ref().server();
1400 ServerSocket & theServerSocket()
1402 // FIXME: this should not be use_gui dependent
1403 BOOST_ASSERT(use_gui);
1404 return LyX::ref().socket();
1408 KeyMap & theTopLevelKeymap()
1410 return LyX::ref().topLevelKeymap();
1414 Converters & theConverters()
1416 return LyX::ref().converters();
1420 Converters & theSystemConverters()
1422 return LyX::ref().systemConverters();
1426 Movers & theMovers()
1428 return LyX::ref().pimpl_->movers_;
1432 Mover const & getMover(std::string const & fmt)
1434 return LyX::ref().pimpl_->movers_(fmt);
1438 void setMover(std::string const & fmt, std::string const & command)
1440 LyX::ref().pimpl_->movers_.set(fmt, command);
1444 Movers & theSystemMovers()
1446 return LyX::ref().pimpl_->system_movers_;
1450 Messages & getMessages(std::string const & language)
1452 return LyX::ref().getMessages(language);
1456 Messages & getGuiMessages()
1458 return LyX::ref().getGuiMessages();