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>
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;
96 std::string geometryArg;
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 support::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 std::string batch_command;
173 frontend::Application * theApp()
176 return singleton_->pimpl_->application_.get();
190 BOOST_ASSERT(singleton_);
195 LyX const & LyX::cref()
197 BOOST_ASSERT(singleton_);
210 BufferList & LyX::bufferList()
212 return pimpl_->buffer_list_;
216 BufferList const & LyX::bufferList() const
218 return pimpl_->buffer_list_;
222 Session & LyX::session()
224 BOOST_ASSERT(pimpl_->session_.get());
225 return *pimpl_->session_.get();
229 Session const & LyX::session() const
231 BOOST_ASSERT(pimpl_->session_.get());
232 return *pimpl_->session_.get();
236 LyXFunc & LyX::lyxFunc()
238 return pimpl_->lyxfunc_;
242 LyXFunc const & LyX::lyxFunc() const
244 return pimpl_->lyxfunc_;
248 Server & LyX::server()
250 BOOST_ASSERT(pimpl_->lyx_server_.get());
251 return *pimpl_->lyx_server_.get();
255 Server const & LyX::server() const
257 BOOST_ASSERT(pimpl_->lyx_server_.get());
258 return *pimpl_->lyx_server_.get();
262 ServerSocket & LyX::socket()
264 BOOST_ASSERT(pimpl_->lyx_socket_.get());
265 return *pimpl_->lyx_socket_.get();
269 ServerSocket const & LyX::socket() const
271 BOOST_ASSERT(pimpl_->lyx_socket_.get());
272 return *pimpl_->lyx_socket_.get();
276 frontend::Application & LyX::application()
278 BOOST_ASSERT(pimpl_->application_.get());
279 return *pimpl_->application_.get();
283 frontend::Application const & LyX::application() const
285 BOOST_ASSERT(pimpl_->application_.get());
286 return *pimpl_->application_.get();
290 KeyMap & LyX::topLevelKeymap()
292 return pimpl_->toplevel_keymap_;
296 CmdDef & LyX::topLevelCmdDef()
298 return pimpl_->toplevel_cmddef_;
302 Converters & LyX::converters()
304 return pimpl_->converters_;
308 Converters & LyX::systemConverters()
310 return pimpl_->system_converters_;
314 KeyMap const & LyX::topLevelKeymap() const
316 return pimpl_->toplevel_keymap_;
320 Messages & LyX::getMessages(std::string const & language)
322 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
324 if (it != pimpl_->messages_.end())
327 std::pair<map<string, Messages>::iterator, bool> result =
328 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
330 BOOST_ASSERT(result.second);
331 return result.first->second;
335 Messages & LyX::getGuiMessages()
337 return pimpl_->messages_["GUI"];
341 void LyX::setGuiLanguage(std::string const & language)
343 pimpl_->messages_["GUI"] = Messages(language);
347 int LyX::exec(int & argc, char * argv[])
349 // Here we need to parse the command line. At least
350 // we need to parse for "-dbg" and "-help"
351 easyParse(argc, argv);
354 support::init_package(to_utf8(from_local8bit(argv[0])),
355 cl_system_support, cl_user_support,
356 support::top_build_dir_is_one_level_up);
357 } catch (support::ExceptionMessage const & message) {
358 if (message.type_ == support::ErrorException) {
359 Alert::error(message.title_, message.details_);
361 } else if (message.type_ == support::WarningException) {
362 Alert::warning(message.title_, message.details_);
366 // Reinit the messages machinery in case package() knows
367 // something interesting about the locale directory.
371 // FIXME: create a ConsoleApplication
372 int exit_status = init(argc, argv);
380 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
385 BufferList::iterator begin = pimpl_->buffer_list_.begin();
387 bool final_success = false;
388 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
390 if (buf != buf->masterBuffer())
392 bool success = false;
393 buf->dispatch(pimpl_->batch_command, &success);
394 final_success |= success;
397 return !final_success;
400 // Let the frontend parse and remove all arguments that it knows
401 pimpl_->application_.reset(createApplication(argc, argv));
403 // Parse and remove all known arguments in the LyX singleton
404 // Give an error for all remaining ones.
405 int exit_status = init(argc, argv);
407 // Kill the application object before exiting.
408 pimpl_->application_.reset();
415 /* Create a CoreApplication class that will provide the main event loop
416 * and the socket callback registering. With Qt4, only QtCore
417 * library would be needed.
418 * When this is done, a server_mode could be created and the following two
419 * line would be moved out from here.
421 // Note: socket callback must be registered after init(argc, argv)
422 // such that package().temp_dir() is properly initialized.
423 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
424 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
425 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
427 // Start the real execution loop.
428 exit_status = pimpl_->application_->exec();
436 void LyX::prepareExit()
438 // Clear the clipboard and selection stack:
439 cap::clearCutStack();
440 cap::clearSelection();
442 // Set a flag that we do quitting from the program,
443 // so no refreshes are necessary.
446 // close buffers first
447 pimpl_->buffer_list_.closeAll();
449 // register session changes and shutdown server and socket
451 if (pimpl_->session_)
452 pimpl_->session_->writeFile();
453 pimpl_->session_.reset();
454 pimpl_->lyx_server_.reset();
455 pimpl_->lyx_socket_.reset();
458 // do any other cleanup procedures now
459 if (package().temp_dir() != package().system_temp_dir()) {
460 LYXERR(Debug::INFO, "Deleting tmp dir "
461 << package().temp_dir().absFilename());
463 if (!package().temp_dir().destroyDirectory()) {
464 docstring const msg =
465 bformat(_("Unable to remove the temporary directory %1$s"),
466 from_utf8(package().temp_dir().absFilename()));
467 Alert::warning(_("Unable to remove temporary directory"), msg);
471 // Kill the application object before exiting. This avoids crashes
472 // when exiting on Linux.
473 if (pimpl_->application_)
474 pimpl_->application_.reset();
478 void LyX::earlyExit(int status)
480 BOOST_ASSERT(pimpl_->application_.get());
481 // LyX::pimpl_::application_ is not initialised at this
482 // point so it's safe to just exit after some cleanup.
488 int LyX::init(int & argc, char * argv[])
490 // check for any spurious extra arguments
491 // other than documents
492 for (int argi = 1; argi < argc ; ++argi) {
493 if (argv[argi][0] == '-') {
495 bformat(_("Wrong command line option `%1$s'. Exiting."),
496 from_utf8(argv[argi]))) << endl;
501 // Initialization of LyX (reads lyxrc and more)
502 LYXERR(Debug::INIT, "Initializing LyX::init...");
503 bool success = init();
504 LYXERR(Debug::INIT, "Initializing LyX::init...done");
508 for (int argi = argc - 1; argi >= 1; --argi) {
509 // get absolute path of file and add ".lyx" to
510 // the filename if necessary
511 pimpl_->files_to_load_.push_back(fileSearch(string(),
512 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
513 "lyx", support::allow_unreadable));
517 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
523 void LyX::addFileToLoad(FileName const & fname)
525 vector<FileName>::const_iterator cit = std::find(
526 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
529 if (cit == pimpl_->files_to_load_.end())
530 pimpl_->files_to_load_.push_back(fname);
534 void LyX::loadFiles()
536 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
537 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
539 for (; it != end; ++it) {
543 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
544 if (buf->loadLyXFile(*it)) {
545 ErrorList const & el = buf->errorList("Parse");
547 for_each(el.begin(), el.end(),
548 boost::bind(&LyX::printError, this, _1));
551 pimpl_->buffer_list_.release(buf);
556 void LyX::execBatchCommands()
558 // The advantage of doing this here is that the event loop
559 // is already started. So any need for interaction will be
563 // if reconfiguration is needed.
564 if (textclasslist.empty()) {
565 switch (Alert::prompt(
566 _("No textclass is found"),
567 _("LyX cannot continue because no textclass is found. "
568 "You can either reconfigure normally, or reconfigure using "
569 "default textclasses, or quit LyX."),
576 // regular reconfigure
577 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
580 // reconfigure --without-latex-config
581 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
582 " --without-latex-config"));
585 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
589 // Execute batch commands if available
590 if (pimpl_->batch_command.empty())
593 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
595 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
599 void LyX::restoreGuiSession()
601 // create the main window
602 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
604 // if there is no valid class list, do not load any file.
605 if (textclasslist.empty())
608 // if some files were specified at command-line we assume that the
609 // user wants to edit *these* files and not to restore the session.
610 if (!pimpl_->files_to_load_.empty()) {
611 for_each(pimpl_->files_to_load_.begin(),
612 pimpl_->files_to_load_.end(),
613 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, true));
614 // clear this list to save a few bytes of RAM
615 pimpl_->files_to_load_.clear();
616 pimpl_->session_->lastOpened().clear();
618 } else if (lyxrc.load_session) {
619 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
620 // do not add to the lastfile list since these files are restored from
621 // last session, and should be already there (regular files), or should
622 // not be added at all (help files).
623 for_each(lastopened.begin(), lastopened.end(),
624 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, false));
626 // clear this list to save a few bytes of RAM
627 pimpl_->session_->lastOpened().clear();
630 BufferList::iterator I = pimpl_->buffer_list_.begin();
631 BufferList::iterator end = pimpl_->buffer_list_.end();
632 for (; I != end; ++I) {
634 if (buf != buf->masterBuffer())
643 The SIGHUP signal does not exist on Windows and does not need to be handled.
645 Windows handles SIGFPE and SIGSEGV signals as expected.
647 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
648 cause a new thread to be spawned. This may well result in unexpected
649 behaviour by the single-threaded LyX.
651 SIGTERM signals will come only from another process actually sending
652 that signal using 'raise' in Windows' POSIX compatability layer. It will
653 not come from the general "terminate process" methods that everyone
654 actually uses (and which can't be trapped). Killing an app 'politely' on
655 Windows involves first sending a WM_CLOSE message, something that is
656 caught already by the Qt frontend.
658 For more information see:
660 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
661 ...signals are mostly useless on Windows for a variety of reasons that are
664 'UNIX Application Migration Guide, Chapter 9'
665 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
667 'How To Terminate an Application "Cleanly" in Win32'
668 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
672 static void error_handler(int err_sig)
674 // Throw away any signals other than the first one received.
675 static sig_atomic_t handling_error = false;
678 handling_error = true;
680 // We have received a signal indicating a fatal error, so
681 // try and save the data ASAP.
682 LyX::cref().emergencyCleanup();
684 // These lyxerr calls may or may not work:
686 // Signals are asynchronous, so the main program may be in a very
687 // fragile state when a signal is processed and thus while a signal
688 // handler function executes.
689 // In general, therefore, we should avoid performing any
690 // I/O operations or calling most library and system functions from
693 // This shouldn't matter here, however, as we've already invoked
698 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
702 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
705 lyxerr << "\nlyx: SIGSEGV signal caught\n"
706 "Sorry, you have found a bug in LyX. "
707 "Please read the bug-reporting instructions "
708 "in Help->Introduction and send us a bug report, "
709 "if necessary. Thanks !\nBye." << endl;
717 // Deinstall the signal handlers
719 signal(SIGHUP, SIG_DFL);
721 signal(SIGINT, SIG_DFL);
722 signal(SIGFPE, SIG_DFL);
723 signal(SIGSEGV, SIG_DFL);
724 signal(SIGTERM, SIG_DFL);
727 if (err_sig == SIGSEGV ||
728 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
730 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
739 void LyX::printError(ErrorItem const & ei)
741 docstring tmp = _("LyX: ") + ei.error + char_type(':')
743 std::cerr << to_utf8(tmp) << std::endl;
750 signal(SIGHUP, error_handler);
752 signal(SIGFPE, error_handler);
753 signal(SIGSEGV, error_handler);
754 signal(SIGINT, error_handler);
755 signal(SIGTERM, error_handler);
756 // SIGPIPE can be safely ignored.
758 lyxrc.tempdir_path = package().temp_dir().absFilename();
759 lyxrc.document_path = package().document_dir().absFilename();
761 if (lyxrc.example_path.empty()) {
762 lyxrc.example_path = addPath(package().system_support().absFilename(),
765 if (lyxrc.template_path.empty()) {
766 lyxrc.template_path = addPath(package().system_support().absFilename(),
771 // Read configuration files
774 // This one may have been distributed along with LyX.
775 if (!readRcFile("lyxrc.dist"))
778 // Set the language defined by the distributor.
779 //setGuiLanguage(lyxrc.gui_language);
781 // Set the PATH correctly.
782 #if !defined (USE_POSIX_PACKAGING)
783 // Add the directory containing the LyX executable to the path
784 // so that LyX can find things like tex2lyx.
785 if (package().build_support().empty())
786 prependEnvPath("PATH", package().binary_dir().absFilename());
788 if (!lyxrc.path_prefix.empty())
789 prependEnvPath("PATH", lyxrc.path_prefix);
791 // Check that user LyX directory is ok.
792 if (queryUserLyXDir(package().explicit_user_support()))
793 reconfigureUserLyXDir();
795 // no need for a splash when there is no GUI
800 // This one is generated in user_support directory by lib/configure.py.
801 if (!readRcFile("lyxrc.defaults"))
804 // Query the OS to know what formats are viewed natively
805 formats.setAutoOpen();
807 // Read lyxrc.dist again to be able to override viewer auto-detection.
808 readRcFile("lyxrc.dist");
810 system_lyxrc = lyxrc;
811 system_formats = formats;
812 pimpl_->system_converters_ = pimpl_->converters_;
813 pimpl_->system_movers_ = pimpl_->movers_;
814 system_lcolor = lcolor;
816 // This one is edited through the preferences dialog.
817 if (!readRcFile("preferences"))
820 if (!readEncodingsFile("encodings", "unicodesymbols"))
822 if (!readLanguagesFile("languages"))
826 LYXERR(Debug::INIT, "Reading layouts...");
832 // read keymap and ui files in batch mode as well
833 // because InsetInfo needs to know these to produce
834 // the correct output
836 // Set the language defined by the user.
837 //setGuiLanguage(lyxrc.gui_language);
839 // Set up command definitions
840 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
843 pimpl_->toplevel_keymap_.read("site");
844 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
845 // load user bind file user.bind
846 pimpl_->toplevel_keymap_.read("user");
848 pimpl_->lyxfunc_.initKeySequences(&pimpl_->toplevel_keymap_);
851 if (!readUIFile(lyxrc.ui_file))
854 if (lyxerr.debugging(Debug::LYXRC))
857 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
858 if (!lyxrc.path_prefix.empty())
859 prependEnvPath("PATH", lyxrc.path_prefix);
861 FileName const document_path(lyxrc.document_path);
862 if (document_path.exists() && document_path.isDirectory())
863 package().document_dir() = document_path;
865 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
866 if (package().temp_dir().empty()) {
867 Alert::error(_("Could not create temporary directory"),
868 bformat(_("Could not create a temporary directory in\n"
869 "%1$s. Make sure that this\n"
870 "path exists and is writable and try again."),
871 from_utf8(lyxrc.tempdir_path)));
872 // createLyXTmpDir() tries sufficiently hard to create a
873 // usable temp dir, so the probability to come here is
874 // close to zero. We therefore don't try to overcome this
875 // problem with e.g. asking the user for a new path and
876 // trying again but simply exit.
880 LYXERR(Debug::INIT, "LyX tmp dir: `"
881 << package().temp_dir().absFilename() << '\'');
883 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
884 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
886 // This must happen after package initialization and after lyxrc is
887 // read, therefore it can't be done by a static object.
888 ConverterCache::init();
894 void LyX::emergencyCleanup() const
896 // what to do about tmpfiles is non-obvious. we would
897 // like to delete any we find, but our lyxdir might
898 // contain documents etc. which might be helpful on
901 pimpl_->buffer_list_.emergencyWriteAll();
903 if (pimpl_->lyx_server_)
904 pimpl_->lyx_server_->emergencyCleanup();
905 pimpl_->lyx_server_.reset();
906 pimpl_->lyx_socket_.reset();
911 void LyX::deadKeyBindings(KeyMap * kbmap)
913 // bindKeyings for transparent handling of deadkeys
914 // The keysyms are gotten from XFree86 X11R6
915 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
916 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
917 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
918 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
919 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
920 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
921 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
922 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
923 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
924 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
925 // nothing with this name
926 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
927 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
928 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
929 // nothing with this name either...
930 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
931 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
932 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
933 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
937 // return true if file does not exist or is older than configure.py.
938 static bool needsUpdate(string const & file)
940 // We cannot initialize configure_script directly because the package
941 // is not initialized yet when static objects are constructed.
942 static FileName configure_script;
943 static bool firstrun = true;
946 FileName(addName(package().system_support().absFilename(),
952 FileName(addName(package().user_support().absFilename(), file));
953 return !absfile.exists()
954 || configure_script.lastModified() > absfile.lastModified();
958 bool LyX::queryUserLyXDir(bool explicit_userdir)
960 // Does user directory exist?
961 FileName const sup = package().user_support();
962 if (sup.exists() && sup.isDirectory()) {
965 return needsUpdate("lyxrc.defaults")
966 || needsUpdate("lyxmodules.lst")
967 || needsUpdate("textclass.lst")
968 || needsUpdate("packages.lst");
971 first_start = !explicit_userdir;
973 // If the user specified explicitly a directory, ask whether
974 // to create it. If the user says "no", then exit.
975 if (explicit_userdir &&
977 _("Missing user LyX directory"),
978 bformat(_("You have specified a non-existent user "
979 "LyX directory, %1$s.\n"
980 "It is needed to keep your own configuration."),
981 from_utf8(package().user_support().absFilename())),
983 _("&Create directory"),
985 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
986 earlyExit(EXIT_FAILURE);
989 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
990 from_utf8(sup.absFilename()))) << endl;
992 if (!sup.createDirectory(0755)) {
993 // Failed, so let's exit.
994 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
996 earlyExit(EXIT_FAILURE);
1003 bool LyX::readRcFile(string const & name)
1005 LYXERR(Debug::INIT, "About to read " << name << "... ");
1007 FileName const lyxrc_path = libFileSearch(string(), name);
1008 if (!lyxrc_path.empty()) {
1009 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1010 if (lyxrc.read(lyxrc_path) < 0) {
1011 showFileError(name);
1015 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1021 // Read the ui file `name'
1022 bool LyX::readUIFile(string const & name, bool include)
1032 struct keyword_item uitags[ui_last - 1] = {
1033 { "include", ui_include },
1034 { "menuset", ui_menuset },
1035 { "toolbars", ui_toolbars },
1036 { "toolbarset", ui_toolbarset }
1039 // Ensure that a file is read only once (prevents include loops)
1040 static std::list<string> uifiles;
1041 std::list<string>::const_iterator it = uifiles.begin();
1042 std::list<string>::const_iterator end = uifiles.end();
1043 it = std::find(it, end, name);
1045 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1046 << "Is this an include loop?");
1050 LYXERR(Debug::INIT, "About to read " << name << "...");
1055 ui_path = libFileSearch("ui", name, "inc");
1056 if (ui_path.empty())
1057 ui_path = libFileSearch("ui",
1058 changeExtension(name, "inc"));
1061 ui_path = libFileSearch("ui", name, "ui");
1063 if (ui_path.empty()) {
1064 LYXERR(Debug::INIT, "Could not find " << name);
1065 showFileError(name);
1069 uifiles.push_back(name);
1071 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1072 Lexer lex(uitags, ui_last - 1);
1073 lex.setFile(ui_path);
1075 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1079 if (lyxerr.debugging(Debug::PARSER))
1080 lex.printTable(lyxerr);
1082 while (lex.isOK()) {
1083 switch (lex.lex()) {
1086 string const file = lex.getString();
1087 if (!readUIFile(file, true))
1092 menubackend.read(lex);
1096 toolbarbackend.readToolbars(lex);
1100 toolbarbackend.readToolbarSettings(lex);
1104 if (!rtrim(lex.getString()).empty())
1105 lex.printError("LyX::ReadUIFile: "
1106 "Unknown menu tag: `$$Token'");
1114 // Read the languages file `name'
1115 bool LyX::readLanguagesFile(string const & name)
1117 LYXERR(Debug::INIT, "About to read " << name << "...");
1119 FileName const lang_path = libFileSearch(string(), name);
1120 if (lang_path.empty()) {
1121 showFileError(name);
1124 languages.read(lang_path);
1129 // Read the encodings file `name'
1130 bool LyX::readEncodingsFile(string const & enc_name,
1131 string const & symbols_name)
1133 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1134 << symbols_name << "...");
1136 FileName const symbols_path = libFileSearch(string(), symbols_name);
1137 if (symbols_path.empty()) {
1138 showFileError(symbols_name);
1142 FileName const enc_path = libFileSearch(string(), enc_name);
1143 if (enc_path.empty()) {
1144 showFileError(enc_name);
1147 encodings.read(enc_path, symbols_path);
1156 /// return the the number of arguments consumed
1157 typedef boost::function<int(string const &, string const &)> cmd_helper;
1159 int parse_dbg(string const & arg, string const &)
1162 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1163 Debug::showTags(lyxerr);
1166 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1168 lyxerr.level(Debug::value(arg));
1169 Debug::showLevel(lyxerr, lyxerr.level());
1174 int parse_help(string const &, string const &)
1177 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1178 "Command line switches (case sensitive):\n"
1179 "\t-help summarize LyX usage\n"
1180 "\t-userdir dir set user directory to dir\n"
1181 "\t-sysdir dir set system directory to dir\n"
1182 "\t-geometry WxH+X+Y set geometry of the main window\n"
1183 "\t-dbg feature[,feature]...\n"
1184 " select the features to debug.\n"
1185 " Type `lyx -dbg' to see the list of features\n"
1186 "\t-x [--execute] command\n"
1187 " where command is a lyx command.\n"
1188 "\t-e [--export] fmt\n"
1189 " where fmt is the export format of choice.\n"
1190 " Look on Tools->Preferences->File formats->Format\n"
1191 " to get an idea which parameters should be passed.\n"
1192 "\t-i [--import] fmt file.xxx\n"
1193 " where fmt is the import format of choice\n"
1194 " and file.xxx is the file to be imported.\n"
1195 "\t-version summarize version and build info\n"
1196 "Check the LyX man page for more details.")) << endl;
1202 int parse_version(string const &, string const &)
1204 lyxerr << "LyX " << lyx_version
1205 << " (" << lyx_release_date << ")" << endl;
1206 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1208 lyxerr << lyx_version_info << endl;
1214 int parse_sysdir(string const & arg, string const &)
1217 Alert::error(_("No system directory"),
1218 _("Missing directory for -sysdir switch"));
1221 cl_system_support = arg;
1226 int parse_userdir(string const & arg, string const &)
1229 Alert::error(_("No user directory"),
1230 _("Missing directory for -userdir switch"));
1233 cl_user_support = arg;
1238 int parse_execute(string const & arg, string const &)
1241 Alert::error(_("Incomplete command"),
1242 _("Missing command string after --execute switch"));
1250 int parse_export(string const & type, string const &)
1253 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1254 "--export switch")) << endl;
1257 batch = "buffer-export " + type;
1263 int parse_import(string const & type, string const & file)
1266 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1267 "--import switch")) << endl;
1271 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1275 batch = "buffer-import " + type + ' ' + file;
1280 int parse_geometry(string const & arg1, string const &)
1283 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1284 // remove also the arg
1287 // don't remove "-geometry"
1296 void LyX::easyParse(int & argc, char * argv[])
1298 std::map<string, cmd_helper> cmdmap;
1300 cmdmap["-dbg"] = parse_dbg;
1301 cmdmap["-help"] = parse_help;
1302 cmdmap["--help"] = parse_help;
1303 cmdmap["-version"] = parse_version;
1304 cmdmap["--version"] = parse_version;
1305 cmdmap["-sysdir"] = parse_sysdir;
1306 cmdmap["-userdir"] = parse_userdir;
1307 cmdmap["-x"] = parse_execute;
1308 cmdmap["--execute"] = parse_execute;
1309 cmdmap["-e"] = parse_export;
1310 cmdmap["--export"] = parse_export;
1311 cmdmap["-i"] = parse_import;
1312 cmdmap["--import"] = parse_import;
1313 cmdmap["-geometry"] = parse_geometry;
1315 for (int i = 1; i < argc; ++i) {
1316 std::map<string, cmd_helper>::const_iterator it
1317 = cmdmap.find(argv[i]);
1319 // don't complain if not found - may be parsed later
1320 if (it == cmdmap.end())
1324 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1326 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1328 int const remove = 1 + it->second(arg, arg2);
1330 // Now, remove used arguments by shifting
1331 // the following ones remove places down.
1334 for (int j = i; j < argc; ++j)
1335 argv[j] = argv[j + remove];
1340 pimpl_->batch_command = batch;
1344 FuncStatus getStatus(FuncRequest const & action)
1346 return LyX::ref().lyxFunc().getStatus(action);
1350 void dispatch(FuncRequest const & action)
1352 LyX::ref().lyxFunc().dispatch(action);
1356 BufferList & theBufferList()
1358 return LyX::ref().bufferList();
1362 LyXFunc & theLyXFunc()
1364 return LyX::ref().lyxFunc();
1368 Server & theServer()
1370 // FIXME: this should not be use_gui dependent
1371 BOOST_ASSERT(use_gui);
1372 return LyX::ref().server();
1376 ServerSocket & theServerSocket()
1378 // FIXME: this should not be use_gui dependent
1379 BOOST_ASSERT(use_gui);
1380 return LyX::ref().socket();
1384 KeyMap & theTopLevelKeymap()
1386 return LyX::ref().topLevelKeymap();
1390 Converters & theConverters()
1392 return LyX::ref().converters();
1396 Converters & theSystemConverters()
1398 return LyX::ref().systemConverters();
1402 Movers & theMovers()
1404 return LyX::ref().pimpl_->movers_;
1408 Mover const & getMover(std::string const & fmt)
1410 return LyX::ref().pimpl_->movers_(fmt);
1414 void setMover(std::string const & fmt, std::string const & command)
1416 LyX::ref().pimpl_->movers_.set(fmt, command);
1420 Movers & theSystemMovers()
1422 return LyX::ref().pimpl_->system_movers_;
1426 Messages & getMessages(std::string const & language)
1428 return LyX::ref().getMessages(language);
1432 Messages & getGuiMessages()
1434 return LyX::ref().getGuiMessages();