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>
74 using namespace lyx::support;
78 namespace Alert = frontend::Alert;
79 namespace os = support::os;
83 // Are we using the GUI at all? We default to true and this is changed
84 // to false when the export feature is used.
88 bool quitting; // flag, that we are quitting the program
92 // Filled with the command line arguments "foo" of "-sysdir foo" or
94 string cl_system_support;
95 string cl_user_support;
101 void showFileError(string const & error)
103 Alert::warning(_("Could not read configuration file"),
104 bformat(_("Error while reading the configuration file\n%1$s.\n"
105 "Please check your installation."), from_utf8(error)));
109 void reconfigureUserLyXDir()
111 string const configure_command = package().configure_command();
113 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
114 PathChanger p(package().user_support());
116 one.startscript(Systemcall::Wait, configure_command);
117 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
123 /// The main application class private implementation.
128 // Set the default User Interface language as soon as possible.
129 // The language used will be derived from the environment
131 messages_["GUI"] = Messages();
133 /// our function handler
136 BufferList buffer_list_;
138 KeyMap toplevel_keymap_;
140 CmdDef toplevel_cmddef_;
142 boost::scoped_ptr<Server> lyx_server_;
144 boost::scoped_ptr<ServerSocket> lyx_socket_;
146 boost::scoped_ptr<frontend::Application> application_;
147 /// lyx session, containing lastfiles, lastfilepos, and lastopened
148 boost::scoped_ptr<Session> session_;
150 /// Files to load at start.
151 vector<FileName> files_to_load_;
153 /// The messages translators.
154 map<string, Messages> messages_;
156 /// The file converters.
157 Converters converters_;
159 // The system converters copy after reading lyxrc.defaults.
160 Converters system_converters_;
165 Movers system_movers_;
167 /// has this user started lyx for the first time?
169 /// the parsed command line batch command if any
170 string batch_command;
174 frontend::Application * theApp()
177 return singleton_->pimpl_->application_.get();
189 void LyX::exit(int exit_code) const
192 // Something wrong happened so better save everything, just in
197 // Properly crash in debug mode in order to get a useful backtrace.
201 // In release mode, try to exit gracefully.
203 theApp()->exit(exit_code);
211 BOOST_ASSERT(singleton_);
216 LyX const & LyX::cref()
218 BOOST_ASSERT(singleton_);
231 BufferList & LyX::bufferList()
233 return pimpl_->buffer_list_;
237 BufferList const & LyX::bufferList() const
239 return pimpl_->buffer_list_;
243 Session & LyX::session()
245 BOOST_ASSERT(pimpl_->session_.get());
246 return *pimpl_->session_.get();
250 Session const & LyX::session() const
252 BOOST_ASSERT(pimpl_->session_.get());
253 return *pimpl_->session_.get();
257 LyXFunc & LyX::lyxFunc()
259 return pimpl_->lyxfunc_;
263 LyXFunc const & LyX::lyxFunc() const
265 return pimpl_->lyxfunc_;
269 Server & LyX::server()
271 BOOST_ASSERT(pimpl_->lyx_server_.get());
272 return *pimpl_->lyx_server_.get();
276 Server const & LyX::server() const
278 BOOST_ASSERT(pimpl_->lyx_server_.get());
279 return *pimpl_->lyx_server_.get();
283 ServerSocket & LyX::socket()
285 BOOST_ASSERT(pimpl_->lyx_socket_.get());
286 return *pimpl_->lyx_socket_.get();
290 ServerSocket const & LyX::socket() const
292 BOOST_ASSERT(pimpl_->lyx_socket_.get());
293 return *pimpl_->lyx_socket_.get();
297 frontend::Application & LyX::application()
299 BOOST_ASSERT(pimpl_->application_.get());
300 return *pimpl_->application_.get();
304 frontend::Application const & LyX::application() const
306 BOOST_ASSERT(pimpl_->application_.get());
307 return *pimpl_->application_.get();
311 KeyMap & LyX::topLevelKeymap()
313 return pimpl_->toplevel_keymap_;
317 CmdDef & LyX::topLevelCmdDef()
319 return pimpl_->toplevel_cmddef_;
323 Converters & LyX::converters()
325 return pimpl_->converters_;
329 Converters & LyX::systemConverters()
331 return pimpl_->system_converters_;
335 KeyMap const & LyX::topLevelKeymap() const
337 return pimpl_->toplevel_keymap_;
341 Messages & LyX::getMessages(string const & language)
343 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
345 if (it != pimpl_->messages_.end())
348 pair<map<string, Messages>::iterator, bool> result =
349 pimpl_->messages_.insert(make_pair(language, Messages(language)));
351 BOOST_ASSERT(result.second);
352 return result.first->second;
356 Messages & LyX::getGuiMessages()
358 return pimpl_->messages_["GUI"];
362 void LyX::setGuiLanguage(string const & language)
364 pimpl_->messages_["GUI"] = Messages(language);
368 int LyX::exec(int & argc, char * argv[])
370 // Here we need to parse the command line. At least
371 // we need to parse for "-dbg" and "-help"
372 easyParse(argc, argv);
375 init_package(to_utf8(from_local8bit(argv[0])),
376 cl_system_support, cl_user_support,
377 top_build_dir_is_one_level_up);
378 } catch (ExceptionMessage const & message) {
379 if (message.type_ == ErrorException) {
380 Alert::error(message.title_, message.details_);
382 } else if (message.type_ == WarningException) {
383 Alert::warning(message.title_, message.details_);
387 // Reinit the messages machinery in case package() knows
388 // something interesting about the locale directory.
392 // FIXME: create a ConsoleApplication
393 int exit_status = init(argc, argv);
401 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
406 BufferList::iterator begin = pimpl_->buffer_list_.begin();
408 bool final_success = false;
409 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
411 if (buf != buf->masterBuffer())
413 bool success = false;
414 buf->dispatch(pimpl_->batch_command, &success);
415 final_success |= success;
418 return !final_success;
421 // Let the frontend parse and remove all arguments that it knows
422 pimpl_->application_.reset(createApplication(argc, argv));
424 // Parse and remove all known arguments in the LyX singleton
425 // Give an error for all remaining ones.
426 int exit_status = init(argc, argv);
428 // Kill the application object before exiting.
429 pimpl_->application_.reset();
436 /* Create a CoreApplication class that will provide the main event loop
437 * and the socket callback registering. With Qt4, only QtCore
438 * library would be needed.
439 * When this is done, a server_mode could be created and the following two
440 * line would be moved out from here.
442 // Note: socket callback must be registered after init(argc, argv)
443 // such that package().temp_dir() is properly initialized.
444 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
445 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
446 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
448 // Start the real execution loop.
449 exit_status = pimpl_->application_->exec();
457 void LyX::prepareExit()
459 // Clear the clipboard and selection stack:
460 cap::clearCutStack();
461 cap::clearSelection();
463 // Set a flag that we do quitting from the program,
464 // so no refreshes are necessary.
467 // close buffers first
468 pimpl_->buffer_list_.closeAll();
470 // register session changes and shutdown server and socket
472 if (pimpl_->session_)
473 pimpl_->session_->writeFile();
474 pimpl_->session_.reset();
475 pimpl_->lyx_server_.reset();
476 pimpl_->lyx_socket_.reset();
479 // do any other cleanup procedures now
480 if (package().temp_dir() != package().system_temp_dir()) {
481 LYXERR(Debug::INFO, "Deleting tmp dir "
482 << package().temp_dir().absFilename());
484 if (!package().temp_dir().destroyDirectory()) {
485 docstring const msg =
486 bformat(_("Unable to remove the temporary directory %1$s"),
487 from_utf8(package().temp_dir().absFilename()));
488 Alert::warning(_("Unable to remove temporary directory"), msg);
492 // Kill the application object before exiting. This avoids crashes
493 // when exiting on Linux.
494 if (pimpl_->application_)
495 pimpl_->application_.reset();
499 void LyX::earlyExit(int status)
501 BOOST_ASSERT(pimpl_->application_.get());
502 // LyX::pimpl_::application_ is not initialised at this
503 // point so it's safe to just exit after some cleanup.
509 int LyX::init(int & argc, char * argv[])
511 // check for any spurious extra arguments
512 // other than documents
513 for (int argi = 1; argi < argc ; ++argi) {
514 if (argv[argi][0] == '-') {
516 bformat(_("Wrong command line option `%1$s'. Exiting."),
517 from_utf8(argv[argi]))) << endl;
522 // Initialization of LyX (reads lyxrc and more)
523 LYXERR(Debug::INIT, "Initializing LyX::init...");
524 bool success = init();
525 LYXERR(Debug::INIT, "Initializing LyX::init...done");
529 for (int argi = argc - 1; argi >= 1; --argi) {
530 // get absolute path of file and add ".lyx" to
531 // the filename if necessary
532 pimpl_->files_to_load_.push_back(fileSearch(string(),
533 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
534 "lyx", allow_unreadable));
538 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
544 void LyX::addFileToLoad(FileName const & fname)
546 vector<FileName>::const_iterator cit = find(
547 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
550 if (cit == pimpl_->files_to_load_.end())
551 pimpl_->files_to_load_.push_back(fname);
555 void LyX::loadFiles()
557 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
558 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
560 for (; it != end; ++it) {
564 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
565 if (buf->loadLyXFile(*it)) {
566 ErrorList const & el = buf->errorList("Parse");
568 for_each(el.begin(), el.end(),
569 boost::bind(&LyX::printError, this, _1));
572 pimpl_->buffer_list_.release(buf);
577 void LyX::execBatchCommands()
579 // The advantage of doing this here is that the event loop
580 // is already started. So any need for interaction will be
584 // if reconfiguration is needed.
585 if (textclasslist.empty()) {
586 switch (Alert::prompt(
587 _("No textclass is found"),
588 _("LyX cannot continue because no textclass is found. "
589 "You can either reconfigure normally, or reconfigure using "
590 "default textclasses, or quit LyX."),
597 // regular reconfigure
598 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
601 // reconfigure --without-latex-config
602 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
603 " --without-latex-config"));
606 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
610 // Execute batch commands if available
611 if (pimpl_->batch_command.empty())
614 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
616 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
620 void LyX::restoreGuiSession()
622 // create the main window
623 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
625 // if there is no valid class list, do not load any file.
626 if (textclasslist.empty())
629 // if some files were specified at command-line we assume that the
630 // user wants to edit *these* files and not to restore the session.
631 if (!pimpl_->files_to_load_.empty()) {
632 for_each(pimpl_->files_to_load_.begin(),
633 pimpl_->files_to_load_.end(),
634 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, true));
635 // clear this list to save a few bytes of RAM
636 pimpl_->files_to_load_.clear();
637 pimpl_->session_->lastOpened().clear();
639 } else if (lyxrc.load_session) {
640 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
641 // do not add to the lastfile list since these files are restored from
642 // last session, and should be already there (regular files), or should
643 // not be added at all (help files).
644 for_each(lastopened.begin(), lastopened.end(),
645 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, false));
647 // clear this list to save a few bytes of RAM
648 pimpl_->session_->lastOpened().clear();
651 BufferList::iterator I = pimpl_->buffer_list_.begin();
652 BufferList::iterator end = pimpl_->buffer_list_.end();
653 for (; I != end; ++I) {
655 if (buf != buf->masterBuffer())
664 The SIGHUP signal does not exist on Windows and does not need to be handled.
666 Windows handles SIGFPE and SIGSEGV signals as expected.
668 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
669 cause a new thread to be spawned. This may well result in unexpected
670 behaviour by the single-threaded LyX.
672 SIGTERM signals will come only from another process actually sending
673 that signal using 'raise' in Windows' POSIX compatability layer. It will
674 not come from the general "terminate process" methods that everyone
675 actually uses (and which can't be trapped). Killing an app 'politely' on
676 Windows involves first sending a WM_CLOSE message, something that is
677 caught already by the Qt frontend.
679 For more information see:
681 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
682 ...signals are mostly useless on Windows for a variety of reasons that are
685 'UNIX Application Migration Guide, Chapter 9'
686 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
688 'How To Terminate an Application "Cleanly" in Win32'
689 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
693 static void error_handler(int err_sig)
695 // Throw away any signals other than the first one received.
696 static sig_atomic_t handling_error = false;
699 handling_error = true;
701 // We have received a signal indicating a fatal error, so
702 // try and save the data ASAP.
703 LyX::cref().emergencyCleanup();
705 // These lyxerr calls may or may not work:
707 // Signals are asynchronous, so the main program may be in a very
708 // fragile state when a signal is processed and thus while a signal
709 // handler function executes.
710 // In general, therefore, we should avoid performing any
711 // I/O operations or calling most library and system functions from
714 // This shouldn't matter here, however, as we've already invoked
719 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
723 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
726 lyxerr << "\nlyx: SIGSEGV signal caught\n"
727 "Sorry, you have found a bug in LyX. "
728 "Please read the bug-reporting instructions "
729 "in Help->Introduction and send us a bug report, "
730 "if necessary. Thanks !\nBye." << endl;
738 // Deinstall the signal handlers
740 signal(SIGHUP, SIG_DFL);
742 signal(SIGINT, SIG_DFL);
743 signal(SIGFPE, SIG_DFL);
744 signal(SIGSEGV, SIG_DFL);
745 signal(SIGTERM, SIG_DFL);
748 if (err_sig == SIGSEGV ||
749 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
751 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
760 void LyX::printError(ErrorItem const & ei)
762 docstring tmp = _("LyX: ") + ei.error + char_type(':')
764 cerr << to_utf8(tmp) << endl;
771 signal(SIGHUP, error_handler);
773 signal(SIGFPE, error_handler);
774 signal(SIGSEGV, error_handler);
775 signal(SIGINT, error_handler);
776 signal(SIGTERM, error_handler);
777 // SIGPIPE can be safely ignored.
779 lyxrc.tempdir_path = package().temp_dir().absFilename();
780 lyxrc.document_path = package().document_dir().absFilename();
782 if (lyxrc.example_path.empty()) {
783 lyxrc.example_path = addPath(package().system_support().absFilename(),
786 if (lyxrc.template_path.empty()) {
787 lyxrc.template_path = addPath(package().system_support().absFilename(),
792 // Read configuration files
795 // This one may have been distributed along with LyX.
796 if (!readRcFile("lyxrc.dist"))
799 // Set the language defined by the distributor.
800 //setGuiLanguage(lyxrc.gui_language);
802 // Set the PATH correctly.
803 #if !defined (USE_POSIX_PACKAGING)
804 // Add the directory containing the LyX executable to the path
805 // so that LyX can find things like tex2lyx.
806 if (package().build_support().empty())
807 prependEnvPath("PATH", package().binary_dir().absFilename());
809 if (!lyxrc.path_prefix.empty())
810 prependEnvPath("PATH", lyxrc.path_prefix);
812 // Check that user LyX directory is ok.
813 if (queryUserLyXDir(package().explicit_user_support()))
814 reconfigureUserLyXDir();
816 // no need for a splash when there is no GUI
821 // This one is generated in user_support directory by lib/configure.py.
822 if (!readRcFile("lyxrc.defaults"))
825 // Query the OS to know what formats are viewed natively
826 formats.setAutoOpen();
828 // Read lyxrc.dist again to be able to override viewer auto-detection.
829 readRcFile("lyxrc.dist");
831 system_lyxrc = lyxrc;
832 system_formats = formats;
833 pimpl_->system_converters_ = pimpl_->converters_;
834 pimpl_->system_movers_ = pimpl_->movers_;
835 system_lcolor = lcolor;
837 // This one is edited through the preferences dialog.
838 if (!readRcFile("preferences"))
841 if (!readEncodingsFile("encodings", "unicodesymbols"))
843 if (!readLanguagesFile("languages"))
847 LYXERR(Debug::INIT, "Reading layouts...");
853 // read keymap and ui files in batch mode as well
854 // because InsetInfo needs to know these to produce
855 // the correct output
857 // Set the language defined by the user.
858 //setGuiLanguage(lyxrc.gui_language);
860 // Set up command definitions
861 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
864 pimpl_->toplevel_keymap_.read("site");
865 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
866 // load user bind file user.bind
867 pimpl_->toplevel_keymap_.read("user");
869 pimpl_->lyxfunc_.initKeySequences(&pimpl_->toplevel_keymap_);
872 if (!readUIFile(lyxrc.ui_file))
875 if (lyxerr.debugging(Debug::LYXRC))
878 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
879 if (!lyxrc.path_prefix.empty())
880 prependEnvPath("PATH", lyxrc.path_prefix);
882 FileName const document_path(lyxrc.document_path);
883 if (document_path.exists() && document_path.isDirectory())
884 package().document_dir() = document_path;
886 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
887 if (package().temp_dir().empty()) {
888 Alert::error(_("Could not create temporary directory"),
889 bformat(_("Could not create a temporary directory in\n"
890 "%1$s. Make sure that this\n"
891 "path exists and is writable and try again."),
892 from_utf8(lyxrc.tempdir_path)));
893 // createLyXTmpDir() tries sufficiently hard to create a
894 // usable temp dir, so the probability to come here is
895 // close to zero. We therefore don't try to overcome this
896 // problem with e.g. asking the user for a new path and
897 // trying again but simply exit.
901 LYXERR(Debug::INIT, "LyX tmp dir: `"
902 << package().temp_dir().absFilename() << '\'');
904 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
905 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
907 // This must happen after package initialization and after lyxrc is
908 // read, therefore it can't be done by a static object.
909 ConverterCache::init();
915 void LyX::emergencyCleanup() const
917 // what to do about tmpfiles is non-obvious. we would
918 // like to delete any we find, but our lyxdir might
919 // contain documents etc. which might be helpful on
922 pimpl_->buffer_list_.emergencyWriteAll();
924 if (pimpl_->lyx_server_)
925 pimpl_->lyx_server_->emergencyCleanup();
926 pimpl_->lyx_server_.reset();
927 pimpl_->lyx_socket_.reset();
932 void LyX::deadKeyBindings(KeyMap * kbmap)
934 // bindKeyings for transparent handling of deadkeys
935 // The keysyms are gotten from XFree86 X11R6
936 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
937 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
938 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
939 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
940 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
941 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
942 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
943 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
944 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
945 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
946 // nothing with this name
947 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
948 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
949 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
950 // nothing with this name either...
951 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
952 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
953 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
954 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
958 // return true if file does not exist or is older than configure.py.
959 static bool needsUpdate(string const & file)
961 // We cannot initialize configure_script directly because the package
962 // is not initialized yet when static objects are constructed.
963 static FileName configure_script;
964 static bool firstrun = true;
967 FileName(addName(package().system_support().absFilename(),
973 FileName(addName(package().user_support().absFilename(), file));
974 return !absfile.exists()
975 || configure_script.lastModified() > absfile.lastModified();
979 bool LyX::queryUserLyXDir(bool explicit_userdir)
981 // Does user directory exist?
982 FileName const sup = package().user_support();
983 if (sup.exists() && sup.isDirectory()) {
986 return needsUpdate("lyxrc.defaults")
987 || needsUpdate("lyxmodules.lst")
988 || needsUpdate("textclass.lst")
989 || needsUpdate("packages.lst");
992 first_start = !explicit_userdir;
994 // If the user specified explicitly a directory, ask whether
995 // to create it. If the user says "no", then exit.
996 if (explicit_userdir &&
998 _("Missing user LyX directory"),
999 bformat(_("You have specified a non-existent user "
1000 "LyX directory, %1$s.\n"
1001 "It is needed to keep your own configuration."),
1002 from_utf8(package().user_support().absFilename())),
1004 _("&Create directory"),
1006 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1007 earlyExit(EXIT_FAILURE);
1010 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1011 from_utf8(sup.absFilename()))) << endl;
1013 if (!sup.createDirectory(0755)) {
1014 // Failed, so let's exit.
1015 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1017 earlyExit(EXIT_FAILURE);
1024 bool LyX::readRcFile(string const & name)
1026 LYXERR(Debug::INIT, "About to read " << name << "... ");
1028 FileName const lyxrc_path = libFileSearch(string(), name);
1029 if (!lyxrc_path.empty()) {
1030 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1031 if (lyxrc.read(lyxrc_path) < 0) {
1032 showFileError(name);
1036 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1042 // Read the ui file `name'
1043 bool LyX::readUIFile(string const & name, bool include)
1053 struct keyword_item uitags[ui_last - 1] = {
1054 { "include", ui_include },
1055 { "menuset", ui_menuset },
1056 { "toolbars", ui_toolbars },
1057 { "toolbarset", ui_toolbarset }
1060 // Ensure that a file is read only once (prevents include loops)
1061 static list<string> uifiles;
1062 list<string>::const_iterator it = uifiles.begin();
1063 list<string>::const_iterator end = uifiles.end();
1064 it = find(it, end, name);
1066 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1067 << "Is this an include loop?");
1071 LYXERR(Debug::INIT, "About to read " << name << "...");
1076 ui_path = libFileSearch("ui", name, "inc");
1077 if (ui_path.empty())
1078 ui_path = libFileSearch("ui",
1079 changeExtension(name, "inc"));
1082 ui_path = libFileSearch("ui", name, "ui");
1084 if (ui_path.empty()) {
1085 LYXERR(Debug::INIT, "Could not find " << name);
1086 showFileError(name);
1090 uifiles.push_back(name);
1092 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1093 Lexer lex(uitags, ui_last - 1);
1094 lex.setFile(ui_path);
1096 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1100 if (lyxerr.debugging(Debug::PARSER))
1101 lex.printTable(lyxerr);
1103 while (lex.isOK()) {
1104 switch (lex.lex()) {
1107 string const file = lex.getString();
1108 if (!readUIFile(file, true))
1113 menubackend.read(lex);
1117 toolbarbackend.readToolbars(lex);
1121 toolbarbackend.readToolbarSettings(lex);
1125 if (!rtrim(lex.getString()).empty())
1126 lex.printError("LyX::ReadUIFile: "
1127 "Unknown menu tag: `$$Token'");
1135 // Read the languages file `name'
1136 bool LyX::readLanguagesFile(string const & name)
1138 LYXERR(Debug::INIT, "About to read " << name << "...");
1140 FileName const lang_path = libFileSearch(string(), name);
1141 if (lang_path.empty()) {
1142 showFileError(name);
1145 languages.read(lang_path);
1150 // Read the encodings file `name'
1151 bool LyX::readEncodingsFile(string const & enc_name,
1152 string const & symbols_name)
1154 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1155 << symbols_name << "...");
1157 FileName const symbols_path = libFileSearch(string(), symbols_name);
1158 if (symbols_path.empty()) {
1159 showFileError(symbols_name);
1163 FileName const enc_path = libFileSearch(string(), enc_name);
1164 if (enc_path.empty()) {
1165 showFileError(enc_name);
1168 encodings.read(enc_path, symbols_path);
1177 /// return the the number of arguments consumed
1178 typedef boost::function<int(string const &, string const &)> cmd_helper;
1180 int parse_dbg(string const & arg, string const &)
1183 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1184 Debug::showTags(lyxerr);
1187 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1189 lyxerr.level(Debug::value(arg));
1190 Debug::showLevel(lyxerr, lyxerr.level());
1195 int parse_help(string const &, string const &)
1198 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1199 "Command line switches (case sensitive):\n"
1200 "\t-help summarize LyX usage\n"
1201 "\t-userdir dir set user directory to dir\n"
1202 "\t-sysdir dir set system directory to dir\n"
1203 "\t-geometry WxH+X+Y set geometry of the main window\n"
1204 "\t-dbg feature[,feature]...\n"
1205 " select the features to debug.\n"
1206 " Type `lyx -dbg' to see the list of features\n"
1207 "\t-x [--execute] command\n"
1208 " where command is a lyx command.\n"
1209 "\t-e [--export] fmt\n"
1210 " where fmt is the export format of choice.\n"
1211 " Look on Tools->Preferences->File formats->Format\n"
1212 " to get an idea which parameters should be passed.\n"
1213 "\t-i [--import] fmt file.xxx\n"
1214 " where fmt is the import format of choice\n"
1215 " and file.xxx is the file to be imported.\n"
1216 "\t-version summarize version and build info\n"
1217 "Check the LyX man page for more details.")) << endl;
1223 int parse_version(string const &, string const &)
1225 lyxerr << "LyX " << lyx_version
1226 << " (" << lyx_release_date << ")" << endl;
1227 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1229 lyxerr << lyx_version_info << endl;
1235 int parse_sysdir(string const & arg, string const &)
1238 Alert::error(_("No system directory"),
1239 _("Missing directory for -sysdir switch"));
1242 cl_system_support = arg;
1247 int parse_userdir(string const & arg, string const &)
1250 Alert::error(_("No user directory"),
1251 _("Missing directory for -userdir switch"));
1254 cl_user_support = arg;
1259 int parse_execute(string const & arg, string const &)
1262 Alert::error(_("Incomplete command"),
1263 _("Missing command string after --execute switch"));
1271 int parse_export(string const & type, string const &)
1274 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1275 "--export switch")) << endl;
1278 batch = "buffer-export " + type;
1284 int parse_import(string const & type, string const & file)
1287 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1288 "--import switch")) << endl;
1292 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1296 batch = "buffer-import " + type + ' ' + file;
1301 int parse_geometry(string const & arg1, string const &)
1304 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1305 // remove also the arg
1308 // don't remove "-geometry"
1317 void LyX::easyParse(int & argc, char * argv[])
1319 map<string, cmd_helper> cmdmap;
1321 cmdmap["-dbg"] = parse_dbg;
1322 cmdmap["-help"] = parse_help;
1323 cmdmap["--help"] = parse_help;
1324 cmdmap["-version"] = parse_version;
1325 cmdmap["--version"] = parse_version;
1326 cmdmap["-sysdir"] = parse_sysdir;
1327 cmdmap["-userdir"] = parse_userdir;
1328 cmdmap["-x"] = parse_execute;
1329 cmdmap["--execute"] = parse_execute;
1330 cmdmap["-e"] = parse_export;
1331 cmdmap["--export"] = parse_export;
1332 cmdmap["-i"] = parse_import;
1333 cmdmap["--import"] = parse_import;
1334 cmdmap["-geometry"] = parse_geometry;
1336 for (int i = 1; i < argc; ++i) {
1337 map<string, cmd_helper>::const_iterator it
1338 = cmdmap.find(argv[i]);
1340 // don't complain if not found - may be parsed later
1341 if (it == cmdmap.end())
1345 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1347 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1349 int const remove = 1 + it->second(arg, arg2);
1351 // Now, remove used arguments by shifting
1352 // the following ones remove places down.
1355 for (int j = i; j < argc; ++j)
1356 argv[j] = argv[j + remove];
1361 pimpl_->batch_command = batch;
1365 FuncStatus getStatus(FuncRequest const & action)
1367 return LyX::ref().lyxFunc().getStatus(action);
1371 void dispatch(FuncRequest const & action)
1373 LyX::ref().lyxFunc().dispatch(action);
1377 BufferList & theBufferList()
1379 return LyX::ref().bufferList();
1383 LyXFunc & theLyXFunc()
1385 return LyX::ref().lyxFunc();
1389 Server & theServer()
1391 // FIXME: this should not be use_gui dependent
1392 BOOST_ASSERT(use_gui);
1393 return LyX::ref().server();
1397 ServerSocket & theServerSocket()
1399 // FIXME: this should not be use_gui dependent
1400 BOOST_ASSERT(use_gui);
1401 return LyX::ref().socket();
1405 KeyMap & theTopLevelKeymap()
1407 return LyX::ref().topLevelKeymap();
1411 Converters & theConverters()
1413 return LyX::ref().converters();
1417 Converters & theSystemConverters()
1419 return LyX::ref().systemConverters();
1423 Movers & theMovers()
1425 return LyX::ref().pimpl_->movers_;
1429 Mover const & getMover(string const & fmt)
1431 return LyX::ref().pimpl_->movers_(fmt);
1435 void setMover(string const & fmt, string const & command)
1437 LyX::ref().pimpl_->movers_.set(fmt, command);
1441 Movers & theSystemMovers()
1443 return LyX::ref().pimpl_->system_movers_;
1447 Messages & getMessages(string const & language)
1449 return LyX::ref().getMessages(language);
1453 Messages & getGuiMessages()
1455 return LyX::ref().getGuiMessages();