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/Messages.h"
56 #include "support/os.h"
57 #include "support/Package.h"
58 #include "support/Path.h"
59 #include "support/Systemcall.h"
61 #include <boost/bind.hpp>
62 #include <boost/scoped_ptr.hpp>
73 using namespace lyx::support;
77 namespace Alert = frontend::Alert;
78 namespace os = support::os;
82 // Are we using the GUI at all? We default to true and this is changed
83 // to false when the export feature is used.
87 bool quitting; // flag, that we are quitting the program
91 // Filled with the command line arguments "foo" of "-sysdir foo" or
93 string cl_system_support;
94 string cl_user_support;
100 void showFileError(string const & error)
102 Alert::warning(_("Could not read configuration file"),
103 bformat(_("Error while reading the configuration file\n%1$s.\n"
104 "Please check your installation."), from_utf8(error)));
108 void reconfigureUserLyXDir()
110 string const configure_command = package().configure_command();
112 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
113 PathChanger p(package().user_support());
115 one.startscript(Systemcall::Wait, configure_command);
116 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
122 /// The main application class private implementation.
127 // Set the default User Interface language as soon as possible.
128 // The language used will be derived from the environment
130 messages_["GUI"] = Messages();
132 /// our function handler
135 BufferList buffer_list_;
137 KeyMap toplevel_keymap_;
139 CmdDef toplevel_cmddef_;
141 boost::scoped_ptr<Server> lyx_server_;
143 boost::scoped_ptr<ServerSocket> lyx_socket_;
145 boost::scoped_ptr<frontend::Application> application_;
146 /// lyx session, containing lastfiles, lastfilepos, and lastopened
147 boost::scoped_ptr<Session> session_;
149 /// Files to load at start.
150 vector<FileName> files_to_load_;
152 /// The messages translators.
153 map<string, Messages> messages_;
155 /// The file converters.
156 Converters converters_;
158 // The system converters copy after reading lyxrc.defaults.
159 Converters system_converters_;
164 Movers system_movers_;
166 /// has this user started lyx for the first time?
168 /// the parsed command line batch command if any
169 string batch_command;
173 frontend::Application * theApp()
176 return singleton_->pimpl_->application_.get();
188 void LyX::exit(int exit_code) const
191 // Something wrong happened so better save everything, just in
196 // Properly crash in debug mode in order to get a useful backtrace.
200 // In release mode, try to exit gracefully.
202 theApp()->exit(exit_code);
210 BOOST_ASSERT(singleton_);
215 LyX const & LyX::cref()
217 BOOST_ASSERT(singleton_);
230 BufferList & LyX::bufferList()
232 return pimpl_->buffer_list_;
236 BufferList const & LyX::bufferList() const
238 return pimpl_->buffer_list_;
242 Session & LyX::session()
244 BOOST_ASSERT(pimpl_->session_.get());
245 return *pimpl_->session_.get();
249 Session const & LyX::session() const
251 BOOST_ASSERT(pimpl_->session_.get());
252 return *pimpl_->session_.get();
256 LyXFunc & LyX::lyxFunc()
258 return pimpl_->lyxfunc_;
262 LyXFunc const & LyX::lyxFunc() const
264 return pimpl_->lyxfunc_;
268 Server & LyX::server()
270 BOOST_ASSERT(pimpl_->lyx_server_.get());
271 return *pimpl_->lyx_server_.get();
275 Server const & LyX::server() const
277 BOOST_ASSERT(pimpl_->lyx_server_.get());
278 return *pimpl_->lyx_server_.get();
282 ServerSocket & LyX::socket()
284 BOOST_ASSERT(pimpl_->lyx_socket_.get());
285 return *pimpl_->lyx_socket_.get();
289 ServerSocket const & LyX::socket() const
291 BOOST_ASSERT(pimpl_->lyx_socket_.get());
292 return *pimpl_->lyx_socket_.get();
296 frontend::Application & LyX::application()
298 BOOST_ASSERT(pimpl_->application_.get());
299 return *pimpl_->application_.get();
303 frontend::Application const & LyX::application() const
305 BOOST_ASSERT(pimpl_->application_.get());
306 return *pimpl_->application_.get();
310 KeyMap & LyX::topLevelKeymap()
312 return pimpl_->toplevel_keymap_;
316 CmdDef & LyX::topLevelCmdDef()
318 return pimpl_->toplevel_cmddef_;
322 Converters & LyX::converters()
324 return pimpl_->converters_;
328 Converters & LyX::systemConverters()
330 return pimpl_->system_converters_;
334 KeyMap const & LyX::topLevelKeymap() const
336 return pimpl_->toplevel_keymap_;
340 Messages & LyX::getMessages(string const & language)
342 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
344 if (it != pimpl_->messages_.end())
347 pair<map<string, Messages>::iterator, bool> result =
348 pimpl_->messages_.insert(make_pair(language, Messages(language)));
350 BOOST_ASSERT(result.second);
351 return result.first->second;
355 Messages & LyX::getGuiMessages()
357 return pimpl_->messages_["GUI"];
361 void LyX::setGuiLanguage(string const & language)
363 pimpl_->messages_["GUI"] = Messages(language);
367 int LyX::exec(int & argc, char * argv[])
369 // Here we need to parse the command line. At least
370 // we need to parse for "-dbg" and "-help"
371 easyParse(argc, argv);
374 init_package(to_utf8(from_local8bit(argv[0])),
375 cl_system_support, cl_user_support,
376 top_build_dir_is_one_level_up);
377 } catch (ExceptionMessage const & message) {
378 if (message.type_ == ErrorException) {
379 Alert::error(message.title_, message.details_);
381 } else if (message.type_ == WarningException) {
382 Alert::warning(message.title_, message.details_);
386 // Reinit the messages machinery in case package() knows
387 // something interesting about the locale directory.
391 // FIXME: create a ConsoleApplication
392 int exit_status = init(argc, argv);
400 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
405 BufferList::iterator begin = pimpl_->buffer_list_.begin();
407 bool final_success = false;
408 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
410 if (buf != buf->masterBuffer())
412 bool success = false;
413 buf->dispatch(pimpl_->batch_command, &success);
414 final_success |= success;
417 return !final_success;
420 // Let the frontend parse and remove all arguments that it knows
421 pimpl_->application_.reset(createApplication(argc, argv));
423 // Parse and remove all known arguments in the LyX singleton
424 // Give an error for all remaining ones.
425 int exit_status = init(argc, argv);
427 // Kill the application object before exiting.
428 pimpl_->application_.reset();
435 /* Create a CoreApplication class that will provide the main event loop
436 * and the socket callback registering. With Qt4, only QtCore
437 * library would be needed.
438 * When this is done, a server_mode could be created and the following two
439 * line would be moved out from here.
441 // Note: socket callback must be registered after init(argc, argv)
442 // such that package().temp_dir() is properly initialized.
443 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
444 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
445 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
447 // Start the real execution loop.
448 exit_status = pimpl_->application_->exec();
456 void LyX::prepareExit()
458 // Clear the clipboard and selection stack:
459 cap::clearCutStack();
460 cap::clearSelection();
462 // Set a flag that we do quitting from the program,
463 // so no refreshes are necessary.
466 // close buffers first
467 pimpl_->buffer_list_.closeAll();
469 // register session changes and shutdown server and socket
471 if (pimpl_->session_)
472 pimpl_->session_->writeFile();
473 pimpl_->session_.reset();
474 pimpl_->lyx_server_.reset();
475 pimpl_->lyx_socket_.reset();
478 // do any other cleanup procedures now
479 if (package().temp_dir() != package().system_temp_dir()) {
480 LYXERR(Debug::INFO, "Deleting tmp dir "
481 << package().temp_dir().absFilename());
483 if (!package().temp_dir().destroyDirectory()) {
484 docstring const msg =
485 bformat(_("Unable to remove the temporary directory %1$s"),
486 from_utf8(package().temp_dir().absFilename()));
487 Alert::warning(_("Unable to remove temporary directory"), msg);
491 // Kill the application object before exiting. This avoids crashes
492 // when exiting on Linux.
493 if (pimpl_->application_)
494 pimpl_->application_.reset();
498 void LyX::earlyExit(int status)
500 BOOST_ASSERT(pimpl_->application_.get());
501 // LyX::pimpl_::application_ is not initialised at this
502 // point so it's safe to just exit after some cleanup.
508 int LyX::init(int & argc, char * argv[])
510 // check for any spurious extra arguments
511 // other than documents
512 for (int argi = 1; argi < argc ; ++argi) {
513 if (argv[argi][0] == '-') {
515 bformat(_("Wrong command line option `%1$s'. Exiting."),
516 from_utf8(argv[argi]))) << endl;
521 // Initialization of LyX (reads lyxrc and more)
522 LYXERR(Debug::INIT, "Initializing LyX::init...");
523 bool success = init();
524 LYXERR(Debug::INIT, "Initializing LyX::init...done");
528 for (int argi = argc - 1; argi >= 1; --argi) {
529 // get absolute path of file and add ".lyx" to
530 // the filename if necessary
531 pimpl_->files_to_load_.push_back(fileSearch(string(),
532 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
533 "lyx", allow_unreadable));
537 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
543 void LyX::addFileToLoad(FileName const & fname)
545 vector<FileName>::const_iterator cit = find(
546 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
549 if (cit == pimpl_->files_to_load_.end())
550 pimpl_->files_to_load_.push_back(fname);
554 void LyX::loadFiles()
556 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
557 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
559 for (; it != end; ++it) {
563 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
564 if (buf->loadLyXFile(*it)) {
565 ErrorList const & el = buf->errorList("Parse");
567 for_each(el.begin(), el.end(),
568 boost::bind(&LyX::printError, this, _1));
571 pimpl_->buffer_list_.release(buf);
576 void LyX::execBatchCommands()
578 // The advantage of doing this here is that the event loop
579 // is already started. So any need for interaction will be
583 // if reconfiguration is needed.
584 if (textclasslist.empty()) {
585 switch (Alert::prompt(
586 _("No textclass is found"),
587 _("LyX cannot continue because no textclass is found. "
588 "You can either reconfigure normally, or reconfigure using "
589 "default textclasses, or quit LyX."),
596 // regular reconfigure
597 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
600 // reconfigure --without-latex-config
601 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
602 " --without-latex-config"));
605 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
609 // Execute batch commands if available
610 if (pimpl_->batch_command.empty())
613 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
615 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
619 void LyX::restoreGuiSession()
621 // create the main window
622 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
624 // if there is no valid class list, do not load any file.
625 if (textclasslist.empty())
628 // if some files were specified at command-line we assume that the
629 // user wants to edit *these* files and not to restore the session.
630 if (!pimpl_->files_to_load_.empty()) {
631 for_each(pimpl_->files_to_load_.begin(),
632 pimpl_->files_to_load_.end(),
633 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, true));
634 // clear this list to save a few bytes of RAM
635 pimpl_->files_to_load_.clear();
636 pimpl_->session_->lastOpened().clear();
638 } else if (lyxrc.load_session) {
639 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
640 // do not add to the lastfile list since these files are restored from
641 // last session, and should be already there (regular files), or should
642 // not be added at all (help files).
643 for_each(lastopened.begin(), lastopened.end(),
644 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, false));
646 // clear this list to save a few bytes of RAM
647 pimpl_->session_->lastOpened().clear();
650 BufferList::iterator I = pimpl_->buffer_list_.begin();
651 BufferList::iterator end = pimpl_->buffer_list_.end();
652 for (; I != end; ++I) {
654 if (buf != buf->masterBuffer())
663 The SIGHUP signal does not exist on Windows and does not need to be handled.
665 Windows handles SIGFPE and SIGSEGV signals as expected.
667 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
668 cause a new thread to be spawned. This may well result in unexpected
669 behaviour by the single-threaded LyX.
671 SIGTERM signals will come only from another process actually sending
672 that signal using 'raise' in Windows' POSIX compatability layer. It will
673 not come from the general "terminate process" methods that everyone
674 actually uses (and which can't be trapped). Killing an app 'politely' on
675 Windows involves first sending a WM_CLOSE message, something that is
676 caught already by the Qt frontend.
678 For more information see:
680 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
681 ...signals are mostly useless on Windows for a variety of reasons that are
684 'UNIX Application Migration Guide, Chapter 9'
685 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
687 'How To Terminate an Application "Cleanly" in Win32'
688 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
692 static void error_handler(int err_sig)
694 // Throw away any signals other than the first one received.
695 static sig_atomic_t handling_error = false;
698 handling_error = true;
700 // We have received a signal indicating a fatal error, so
701 // try and save the data ASAP.
702 LyX::cref().emergencyCleanup();
704 // These lyxerr calls may or may not work:
706 // Signals are asynchronous, so the main program may be in a very
707 // fragile state when a signal is processed and thus while a signal
708 // handler function executes.
709 // In general, therefore, we should avoid performing any
710 // I/O operations or calling most library and system functions from
713 // This shouldn't matter here, however, as we've already invoked
718 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
722 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
725 lyxerr << "\nlyx: SIGSEGV signal caught\n"
726 "Sorry, you have found a bug in LyX. "
727 "Please read the bug-reporting instructions "
728 "in Help->Introduction and send us a bug report, "
729 "if necessary. Thanks !\nBye." << endl;
737 // Deinstall the signal handlers
739 signal(SIGHUP, SIG_DFL);
741 signal(SIGINT, SIG_DFL);
742 signal(SIGFPE, SIG_DFL);
743 signal(SIGSEGV, SIG_DFL);
744 signal(SIGTERM, SIG_DFL);
747 if (err_sig == SIGSEGV ||
748 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
750 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
759 void LyX::printError(ErrorItem const & ei)
761 docstring tmp = _("LyX: ") + ei.error + char_type(':')
763 cerr << to_utf8(tmp) << endl;
770 signal(SIGHUP, error_handler);
772 signal(SIGFPE, error_handler);
773 signal(SIGSEGV, error_handler);
774 signal(SIGINT, error_handler);
775 signal(SIGTERM, error_handler);
776 // SIGPIPE can be safely ignored.
778 lyxrc.tempdir_path = package().temp_dir().absFilename();
779 lyxrc.document_path = package().document_dir().absFilename();
781 if (lyxrc.example_path.empty()) {
782 lyxrc.example_path = addPath(package().system_support().absFilename(),
785 if (lyxrc.template_path.empty()) {
786 lyxrc.template_path = addPath(package().system_support().absFilename(),
791 // Read configuration files
794 // This one may have been distributed along with LyX.
795 if (!readRcFile("lyxrc.dist"))
798 // Set the language defined by the distributor.
799 //setGuiLanguage(lyxrc.gui_language);
801 // Set the PATH correctly.
802 #if !defined (USE_POSIX_PACKAGING)
803 // Add the directory containing the LyX executable to the path
804 // so that LyX can find things like tex2lyx.
805 if (package().build_support().empty())
806 prependEnvPath("PATH", package().binary_dir().absFilename());
808 if (!lyxrc.path_prefix.empty())
809 prependEnvPath("PATH", lyxrc.path_prefix);
811 // Check that user LyX directory is ok.
812 if (queryUserLyXDir(package().explicit_user_support()))
813 reconfigureUserLyXDir();
815 // no need for a splash when there is no GUI
820 // This one is generated in user_support directory by lib/configure.py.
821 if (!readRcFile("lyxrc.defaults"))
824 // Query the OS to know what formats are viewed natively
825 formats.setAutoOpen();
827 // Read lyxrc.dist again to be able to override viewer auto-detection.
828 readRcFile("lyxrc.dist");
830 system_lyxrc = lyxrc;
831 system_formats = formats;
832 pimpl_->system_converters_ = pimpl_->converters_;
833 pimpl_->system_movers_ = pimpl_->movers_;
834 system_lcolor = lcolor;
836 // This one is edited through the preferences dialog.
837 if (!readRcFile("preferences"))
840 if (!readEncodingsFile("encodings", "unicodesymbols"))
842 if (!readLanguagesFile("languages"))
846 LYXERR(Debug::INIT, "Reading layouts...");
852 // read keymap and ui files in batch mode as well
853 // because InsetInfo needs to know these to produce
854 // the correct output
856 // Set the language defined by the user.
857 //setGuiLanguage(lyxrc.gui_language);
859 // Set up command definitions
860 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
863 pimpl_->toplevel_keymap_.read("site");
864 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
865 // load user bind file user.bind
866 pimpl_->toplevel_keymap_.read("user");
868 pimpl_->lyxfunc_.initKeySequences(&pimpl_->toplevel_keymap_);
871 if (!readUIFile(lyxrc.ui_file))
874 if (lyxerr.debugging(Debug::LYXRC))
877 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
878 if (!lyxrc.path_prefix.empty())
879 prependEnvPath("PATH", lyxrc.path_prefix);
881 FileName const document_path(lyxrc.document_path);
882 if (document_path.exists() && document_path.isDirectory())
883 package().document_dir() = document_path;
885 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
886 if (package().temp_dir().empty()) {
887 Alert::error(_("Could not create temporary directory"),
888 bformat(_("Could not create a temporary directory in\n"
889 "%1$s. Make sure that this\n"
890 "path exists and is writable and try again."),
891 from_utf8(lyxrc.tempdir_path)));
892 // createLyXTmpDir() tries sufficiently hard to create a
893 // usable temp dir, so the probability to come here is
894 // close to zero. We therefore don't try to overcome this
895 // problem with e.g. asking the user for a new path and
896 // trying again but simply exit.
900 LYXERR(Debug::INIT, "LyX tmp dir: `"
901 << package().temp_dir().absFilename() << '\'');
903 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
904 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
906 // This must happen after package initialization and after lyxrc is
907 // read, therefore it can't be done by a static object.
908 ConverterCache::init();
914 void LyX::emergencyCleanup() const
916 // what to do about tmpfiles is non-obvious. we would
917 // like to delete any we find, but our lyxdir might
918 // contain documents etc. which might be helpful on
921 pimpl_->buffer_list_.emergencyWriteAll();
923 if (pimpl_->lyx_server_)
924 pimpl_->lyx_server_->emergencyCleanup();
925 pimpl_->lyx_server_.reset();
926 pimpl_->lyx_socket_.reset();
931 void LyX::deadKeyBindings(KeyMap * kbmap)
933 // bindKeyings for transparent handling of deadkeys
934 // The keysyms are gotten from XFree86 X11R6
935 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
936 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
937 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
938 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
939 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
940 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
941 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
942 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
943 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
944 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
945 // nothing with this name
946 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
947 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
948 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
949 // nothing with this name either...
950 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
951 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
952 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
953 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
957 // return true if file does not exist or is older than configure.py.
958 static bool needsUpdate(string const & file)
960 // We cannot initialize configure_script directly because the package
961 // is not initialized yet when static objects are constructed.
962 static FileName configure_script;
963 static bool firstrun = true;
966 FileName(addName(package().system_support().absFilename(),
972 FileName(addName(package().user_support().absFilename(), file));
973 return !absfile.exists()
974 || configure_script.lastModified() > absfile.lastModified();
978 bool LyX::queryUserLyXDir(bool explicit_userdir)
980 // Does user directory exist?
981 FileName const sup = package().user_support();
982 if (sup.exists() && sup.isDirectory()) {
985 return needsUpdate("lyxrc.defaults")
986 || needsUpdate("lyxmodules.lst")
987 || needsUpdate("textclass.lst")
988 || needsUpdate("packages.lst");
991 first_start = !explicit_userdir;
993 // If the user specified explicitly a directory, ask whether
994 // to create it. If the user says "no", then exit.
995 if (explicit_userdir &&
997 _("Missing user LyX directory"),
998 bformat(_("You have specified a non-existent user "
999 "LyX directory, %1$s.\n"
1000 "It is needed to keep your own configuration."),
1001 from_utf8(package().user_support().absFilename())),
1003 _("&Create directory"),
1005 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1006 earlyExit(EXIT_FAILURE);
1009 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1010 from_utf8(sup.absFilename()))) << endl;
1012 if (!sup.createDirectory(0755)) {
1013 // Failed, so let's exit.
1014 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1016 earlyExit(EXIT_FAILURE);
1023 bool LyX::readRcFile(string const & name)
1025 LYXERR(Debug::INIT, "About to read " << name << "... ");
1027 FileName const lyxrc_path = libFileSearch(string(), name);
1028 if (!lyxrc_path.empty()) {
1029 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1030 if (lyxrc.read(lyxrc_path) < 0) {
1031 showFileError(name);
1035 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1041 // Read the ui file `name'
1042 bool LyX::readUIFile(string const & name, bool include)
1052 struct keyword_item uitags[ui_last - 1] = {
1053 { "include", ui_include },
1054 { "menuset", ui_menuset },
1055 { "toolbars", ui_toolbars },
1056 { "toolbarset", ui_toolbarset }
1059 // Ensure that a file is read only once (prevents include loops)
1060 static list<string> uifiles;
1061 list<string>::const_iterator it = uifiles.begin();
1062 list<string>::const_iterator end = uifiles.end();
1063 it = find(it, end, name);
1065 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1066 << "Is this an include loop?");
1070 LYXERR(Debug::INIT, "About to read " << name << "...");
1075 ui_path = libFileSearch("ui", name, "inc");
1076 if (ui_path.empty())
1077 ui_path = libFileSearch("ui",
1078 changeExtension(name, "inc"));
1081 ui_path = libFileSearch("ui", name, "ui");
1083 if (ui_path.empty()) {
1084 LYXERR(Debug::INIT, "Could not find " << name);
1085 showFileError(name);
1089 uifiles.push_back(name);
1091 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1092 Lexer lex(uitags, ui_last - 1);
1093 lex.setFile(ui_path);
1095 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1099 if (lyxerr.debugging(Debug::PARSER))
1100 lex.printTable(lyxerr);
1102 while (lex.isOK()) {
1103 switch (lex.lex()) {
1106 string const file = lex.getString();
1107 if (!readUIFile(file, true))
1112 theApp()->menuBackend().read(lex);
1116 toolbarbackend.readToolbars(lex);
1120 toolbarbackend.readToolbarSettings(lex);
1124 if (!rtrim(lex.getString()).empty())
1125 lex.printError("LyX::ReadUIFile: "
1126 "Unknown menu tag: `$$Token'");
1134 // Read the languages file `name'
1135 bool LyX::readLanguagesFile(string const & name)
1137 LYXERR(Debug::INIT, "About to read " << name << "...");
1139 FileName const lang_path = libFileSearch(string(), name);
1140 if (lang_path.empty()) {
1141 showFileError(name);
1144 languages.read(lang_path);
1149 // Read the encodings file `name'
1150 bool LyX::readEncodingsFile(string const & enc_name,
1151 string const & symbols_name)
1153 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1154 << symbols_name << "...");
1156 FileName const symbols_path = libFileSearch(string(), symbols_name);
1157 if (symbols_path.empty()) {
1158 showFileError(symbols_name);
1162 FileName const enc_path = libFileSearch(string(), enc_name);
1163 if (enc_path.empty()) {
1164 showFileError(enc_name);
1167 encodings.read(enc_path, symbols_path);
1176 /// return the the number of arguments consumed
1177 typedef boost::function<int(string const &, string const &)> cmd_helper;
1179 int parse_dbg(string const & arg, string const &)
1182 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1183 Debug::showTags(lyxerr);
1186 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1188 lyxerr.level(Debug::value(arg));
1189 Debug::showLevel(lyxerr, lyxerr.level());
1194 int parse_help(string const &, string const &)
1197 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1198 "Command line switches (case sensitive):\n"
1199 "\t-help summarize LyX usage\n"
1200 "\t-userdir dir set user directory to dir\n"
1201 "\t-sysdir dir set system directory to dir\n"
1202 "\t-geometry WxH+X+Y set geometry of the main window\n"
1203 "\t-dbg feature[,feature]...\n"
1204 " select the features to debug.\n"
1205 " Type `lyx -dbg' to see the list of features\n"
1206 "\t-x [--execute] command\n"
1207 " where command is a lyx command.\n"
1208 "\t-e [--export] fmt\n"
1209 " where fmt is the export format of choice.\n"
1210 " Look on Tools->Preferences->File formats->Format\n"
1211 " to get an idea which parameters should be passed.\n"
1212 "\t-i [--import] fmt file.xxx\n"
1213 " where fmt is the import format of choice\n"
1214 " and file.xxx is the file to be imported.\n"
1215 "\t-version summarize version and build info\n"
1216 "Check the LyX man page for more details.")) << endl;
1222 int parse_version(string const &, string const &)
1224 lyxerr << "LyX " << lyx_version
1225 << " (" << lyx_release_date << ")" << endl;
1226 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1228 lyxerr << lyx_version_info << endl;
1234 int parse_sysdir(string const & arg, string const &)
1237 Alert::error(_("No system directory"),
1238 _("Missing directory for -sysdir switch"));
1241 cl_system_support = arg;
1246 int parse_userdir(string const & arg, string const &)
1249 Alert::error(_("No user directory"),
1250 _("Missing directory for -userdir switch"));
1253 cl_user_support = arg;
1258 int parse_execute(string const & arg, string const &)
1261 Alert::error(_("Incomplete command"),
1262 _("Missing command string after --execute switch"));
1270 int parse_export(string const & type, string const &)
1273 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1274 "--export switch")) << endl;
1277 batch = "buffer-export " + type;
1283 int parse_import(string const & type, string const & file)
1286 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1287 "--import switch")) << endl;
1291 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1295 batch = "buffer-import " + type + ' ' + file;
1300 int parse_geometry(string const & arg1, string const &)
1303 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1304 // remove also the arg
1307 // don't remove "-geometry"
1316 void LyX::easyParse(int & argc, char * argv[])
1318 map<string, cmd_helper> cmdmap;
1320 cmdmap["-dbg"] = parse_dbg;
1321 cmdmap["-help"] = parse_help;
1322 cmdmap["--help"] = parse_help;
1323 cmdmap["-version"] = parse_version;
1324 cmdmap["--version"] = parse_version;
1325 cmdmap["-sysdir"] = parse_sysdir;
1326 cmdmap["-userdir"] = parse_userdir;
1327 cmdmap["-x"] = parse_execute;
1328 cmdmap["--execute"] = parse_execute;
1329 cmdmap["-e"] = parse_export;
1330 cmdmap["--export"] = parse_export;
1331 cmdmap["-i"] = parse_import;
1332 cmdmap["--import"] = parse_import;
1333 cmdmap["-geometry"] = parse_geometry;
1335 for (int i = 1; i < argc; ++i) {
1336 map<string, cmd_helper>::const_iterator it
1337 = cmdmap.find(argv[i]);
1339 // don't complain if not found - may be parsed later
1340 if (it == cmdmap.end())
1344 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1346 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1348 int const remove = 1 + it->second(arg, arg2);
1350 // Now, remove used arguments by shifting
1351 // the following ones remove places down.
1354 for (int j = i; j < argc; ++j)
1355 argv[j] = argv[j + remove];
1360 pimpl_->batch_command = batch;
1364 FuncStatus getStatus(FuncRequest const & action)
1366 return LyX::ref().lyxFunc().getStatus(action);
1370 void dispatch(FuncRequest const & action)
1372 LyX::ref().lyxFunc().dispatch(action);
1376 BufferList & theBufferList()
1378 return LyX::ref().bufferList();
1382 LyXFunc & theLyXFunc()
1384 return LyX::ref().lyxFunc();
1388 Server & theServer()
1390 // FIXME: this should not be use_gui dependent
1391 BOOST_ASSERT(use_gui);
1392 return LyX::ref().server();
1396 ServerSocket & theServerSocket()
1398 // FIXME: this should not be use_gui dependent
1399 BOOST_ASSERT(use_gui);
1400 return LyX::ref().socket();
1404 KeyMap & theTopLevelKeymap()
1406 return LyX::ref().topLevelKeymap();
1410 Converters & theConverters()
1412 return LyX::ref().converters();
1416 Converters & theSystemConverters()
1418 return LyX::ref().systemConverters();
1422 Movers & theMovers()
1424 return LyX::ref().pimpl_->movers_;
1428 Mover const & getMover(string const & fmt)
1430 return LyX::ref().pimpl_->movers_(fmt);
1434 void setMover(string const & fmt, string const & command)
1436 LyX::ref().pimpl_->movers_.set(fmt, command);
1440 Movers & theSystemMovers()
1442 return LyX::ref().pimpl_->system_movers_;
1446 Messages & getMessages(string const & language)
1448 return LyX::ref().getMessages(language);
1452 Messages & getGuiMessages()
1454 return LyX::ref().getGuiMessages();