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.
21 #include "ConverterCache.h"
23 #include "buffer_funcs.h"
24 #include "BufferList.h"
25 #include "Converter.h"
26 #include "CutAndPaste.h"
29 #include "ErrorList.h"
36 #include "LyXAction.h"
40 #include "ModuleList.h"
42 #include "ServerSocket.h"
43 #include "TextClassList.h"
44 #include "MenuBackend.h"
47 #include "ToolbarBackend.h"
49 #include "frontends/alert.h"
50 #include "frontends/Application.h"
52 #include "support/environment.h"
53 #include "support/filetools.h"
54 #include "support/lstrings.h"
55 #include "support/lyxlib.h"
56 #include "support/ExceptionMessage.h"
57 #include "support/os.h"
58 #include "support/Package.h"
59 #include "support/Path.h"
60 #include "support/Systemcall.h"
62 #include <boost/bind.hpp>
63 #include <boost/scoped_ptr.hpp>
79 #ifndef CXX_GLOBAL_CSTD
87 using support::addName;
88 using support::addPath;
89 using support::bformat;
90 using support::changeExtension;
91 using support::createLyXTmpDir;
92 using support::FileName;
93 using support::fileSearch;
94 using support::getEnv;
95 using support::i18nLibFileSearch;
96 using support::libFileSearch;
97 using support::package;
98 using support::prependEnvPath;
100 using support::Systemcall;
102 namespace Alert = frontend::Alert;
103 namespace os = support::os;
107 // Are we using the GUI at all? We default to true and this is changed
108 // to false when the export feature is used.
112 bool quitting; // flag, that we are quitting the program
116 // Filled with the command line arguments "foo" of "-sysdir foo" or
118 string cl_system_support;
119 string cl_user_support;
121 std::string geometryArg;
123 LyX * singleton_ = 0;
125 void showFileError(string const & error)
127 Alert::warning(_("Could not read configuration file"),
128 bformat(_("Error while reading the configuration file\n%1$s.\n"
129 "Please check your installation."), from_utf8(error)));
133 void reconfigureUserLyXDir()
135 string const configure_command = package().configure_command();
137 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
138 support::PathChanger p(package().user_support());
140 one.startscript(Systemcall::Wait, configure_command);
141 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
147 /// The main application class private implementation.
152 // Set the default User Interface language as soon as possible.
153 // The language used will be derived from the environment
155 messages_["GUI"] = Messages();
157 /// our function handler
160 BufferList buffer_list_;
162 boost::scoped_ptr<KeyMap> toplevel_keymap_;
164 boost::scoped_ptr<CmdDef> toplevel_cmddef_;
166 boost::scoped_ptr<Server> lyx_server_;
168 boost::scoped_ptr<ServerSocket> lyx_socket_;
170 boost::scoped_ptr<frontend::Application> application_;
171 /// lyx session, containing lastfiles, lastfilepos, and lastopened
172 boost::scoped_ptr<Session> session_;
174 /// Files to load at start.
175 vector<FileName> files_to_load_;
177 /// The messages translators.
178 map<string, Messages> messages_;
180 /// The file converters.
181 Converters converters_;
183 // The system converters copy after reading lyxrc.defaults.
184 Converters system_converters_;
189 Movers system_movers_;
191 /// has this user started lyx for the first time?
193 /// the parsed command line batch command if any
194 std::string batch_command;
198 frontend::Application * theApp()
201 return singleton_->pimpl_->application_.get();
215 BOOST_ASSERT(singleton_);
220 LyX const & LyX::cref()
222 BOOST_ASSERT(singleton_);
235 BufferList & LyX::bufferList()
237 return pimpl_->buffer_list_;
241 BufferList const & LyX::bufferList() const
243 return pimpl_->buffer_list_;
247 Session & LyX::session()
249 BOOST_ASSERT(pimpl_->session_.get());
250 return *pimpl_->session_.get();
254 Session const & LyX::session() const
256 BOOST_ASSERT(pimpl_->session_.get());
257 return *pimpl_->session_.get();
261 LyXFunc & LyX::lyxFunc()
263 return pimpl_->lyxfunc_;
267 LyXFunc const & LyX::lyxFunc() const
269 return pimpl_->lyxfunc_;
273 Server & LyX::server()
275 BOOST_ASSERT(pimpl_->lyx_server_.get());
276 return *pimpl_->lyx_server_.get();
280 Server const & LyX::server() const
282 BOOST_ASSERT(pimpl_->lyx_server_.get());
283 return *pimpl_->lyx_server_.get();
287 ServerSocket & LyX::socket()
289 BOOST_ASSERT(pimpl_->lyx_socket_.get());
290 return *pimpl_->lyx_socket_.get();
294 ServerSocket const & LyX::socket() const
296 BOOST_ASSERT(pimpl_->lyx_socket_.get());
297 return *pimpl_->lyx_socket_.get();
301 frontend::Application & LyX::application()
303 BOOST_ASSERT(pimpl_->application_.get());
304 return *pimpl_->application_.get();
308 frontend::Application const & LyX::application() const
310 BOOST_ASSERT(pimpl_->application_.get());
311 return *pimpl_->application_.get();
315 KeyMap & LyX::topLevelKeymap()
317 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
318 return *pimpl_->toplevel_keymap_.get();
322 CmdDef & LyX::topLevelCmdDef()
324 BOOST_ASSERT(pimpl_->toplevel_cmddef_.get());
325 return *pimpl_->toplevel_cmddef_.get();
329 Converters & LyX::converters()
331 return pimpl_->converters_;
335 Converters & LyX::systemConverters()
337 return pimpl_->system_converters_;
341 KeyMap const & LyX::topLevelKeymap() const
343 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
344 return *pimpl_->toplevel_keymap_.get();
348 Messages & LyX::getMessages(std::string const & language)
350 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
352 if (it != pimpl_->messages_.end())
355 std::pair<map<string, Messages>::iterator, bool> result =
356 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
358 BOOST_ASSERT(result.second);
359 return result.first->second;
363 Messages & LyX::getGuiMessages()
365 return pimpl_->messages_["GUI"];
369 void LyX::setGuiLanguage(std::string const & language)
371 pimpl_->messages_["GUI"] = Messages(language);
375 int LyX::exec(int & argc, char * argv[])
377 // Here we need to parse the command line. At least
378 // we need to parse for "-dbg" and "-help"
379 easyParse(argc, argv);
382 support::init_package(to_utf8(from_local8bit(argv[0])),
383 cl_system_support, cl_user_support,
384 support::top_build_dir_is_one_level_up);
385 } catch (support::ExceptionMessage const & message) {
386 if (message.type_ == support::ErrorException) {
387 Alert::error(message.title_, message.details_);
389 } else if (message.type_ == support::WarningException) {
390 Alert::warning(message.title_, message.details_);
394 // Reinit the messages machinery in case package() knows
395 // something interesting about the locale directory.
399 // FIXME: create a ConsoleApplication
400 int exit_status = init(argc, argv);
408 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
413 BufferList::iterator begin = pimpl_->buffer_list_.begin();
415 bool final_success = false;
416 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
418 if (buf != buf->masterBuffer())
420 bool success = false;
421 buf->dispatch(pimpl_->batch_command, &success);
422 final_success |= success;
425 return !final_success;
428 // Let the frontend parse and remove all arguments that it knows
429 pimpl_->application_.reset(createApplication(argc, argv));
431 // Parse and remove all known arguments in the LyX singleton
432 // Give an error for all remaining ones.
433 int exit_status = init(argc, argv);
435 // Kill the application object before exiting.
436 pimpl_->application_.reset();
443 /* Create a CoreApplication class that will provide the main event loop
444 * and the socket callback registering. With Qt4, only QtCore
445 * library would be needed.
446 * When this is done, a server_mode could be created and the following two
447 * line would be moved out from here.
449 // Note: socket callback must be registered after init(argc, argv)
450 // such that package().temp_dir() is properly initialized.
451 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
452 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
453 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
455 // Start the real execution loop.
456 exit_status = pimpl_->application_->exec();
464 void LyX::prepareExit()
466 // Clear the clipboard and selection stack:
467 cap::clearCutStack();
468 cap::clearSelection();
470 // Set a flag that we do quitting from the program,
471 // so no refreshes are necessary.
474 // close buffers first
475 pimpl_->buffer_list_.closeAll();
477 // do any other cleanup procedures now
478 if (package().temp_dir() != package().system_temp_dir()) {
479 LYXERR(Debug::INFO, "Deleting tmp dir "
480 << package().temp_dir().absFilename());
482 if (!package().temp_dir().destroyDirectory()) {
483 docstring const msg =
484 bformat(_("Unable to remove the temporary directory %1$s"),
485 from_utf8(package().temp_dir().absFilename()));
486 Alert::warning(_("Unable to remove temporary directory"), msg);
491 if (pimpl_->session_)
492 pimpl_->session_->writeFile();
493 pimpl_->session_.reset();
494 pimpl_->lyx_server_.reset();
495 pimpl_->lyx_socket_.reset();
498 // Kill the application object before exiting. This avoids crashes
499 // when exiting on Linux.
500 if (pimpl_->application_)
501 pimpl_->application_.reset();
505 void LyX::earlyExit(int status)
507 BOOST_ASSERT(pimpl_->application_.get());
508 // LyX::pimpl_::application_ is not initialised at this
509 // point so it's safe to just exit after some cleanup.
515 int LyX::init(int & argc, char * argv[])
517 // check for any spurious extra arguments
518 // other than documents
519 for (int argi = 1; argi < argc ; ++argi) {
520 if (argv[argi][0] == '-') {
522 bformat(_("Wrong command line option `%1$s'. Exiting."),
523 from_utf8(argv[argi]))) << endl;
528 // Initialization of LyX (reads lyxrc and more)
529 LYXERR(Debug::INIT, "Initializing LyX::init...");
530 bool success = init();
531 LYXERR(Debug::INIT, "Initializing LyX::init...done");
535 for (int argi = argc - 1; argi >= 1; --argi) {
536 // get absolute path of file and add ".lyx" to
537 // the filename if necessary
538 pimpl_->files_to_load_.push_back(fileSearch(string(),
539 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
540 "lyx", support::allow_unreadable));
544 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
550 void LyX::addFileToLoad(FileName const & fname)
552 vector<FileName>::const_iterator cit = std::find(
553 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
556 if (cit == pimpl_->files_to_load_.end())
557 pimpl_->files_to_load_.push_back(fname);
561 void LyX::loadFiles()
563 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
564 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
566 for (; it != end; ++it) {
570 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
571 if (buf->loadLyXFile(*it)) {
572 ErrorList const & el = buf->errorList("Parse");
574 for_each(el.begin(), el.end(),
575 boost::bind(&LyX::printError, this, _1));
578 pimpl_->buffer_list_.release(buf);
583 void LyX::execBatchCommands()
585 // The advantage of doing this here is that the event loop
586 // is already started. So any need for interaction will be
590 // if reconfiguration is needed.
591 if (textclasslist.empty()) {
592 switch (Alert::prompt(
593 _("No textclass is found"),
594 _("LyX cannot continue because no textclass is found. "
595 "You can either reconfigure normally, or reconfigure using "
596 "default textclasses, or quit LyX."),
603 // regular reconfigure
604 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
607 // reconfigure --without-latex-config
608 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
609 " --without-latex-config"));
612 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
616 // Execute batch commands if available
617 if (pimpl_->batch_command.empty())
620 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
622 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
626 void LyX::restoreGuiSession()
628 // create the main window
629 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
631 // if there is no valid class list, do not load any file.
632 if (textclasslist.empty())
635 // if some files were specified at command-line we assume that the
636 // user wants to edit *these* files and not to restore the session.
637 if (!pimpl_->files_to_load_.empty()) {
638 for_each(pimpl_->files_to_load_.begin(),
639 pimpl_->files_to_load_.end(),
640 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, true));
641 // clear this list to save a few bytes of RAM
642 pimpl_->files_to_load_.clear();
643 pimpl_->session_->lastOpened().clear();
645 } else if (lyxrc.load_session) {
646 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
647 // do not add to the lastfile list since these files are restored from
648 // last session, and should be already there (regular files), or should
649 // not be added at all (help files).
650 for_each(lastopened.begin(), lastopened.end(),
651 bind(&LyXFunc::loadAndViewFile, pimpl_->lyxfunc_, _1, false));
653 // clear this list to save a few bytes of RAM
654 pimpl_->session_->lastOpened().clear();
657 BufferList::iterator I = pimpl_->buffer_list_.begin();
658 BufferList::iterator end = pimpl_->buffer_list_.end();
659 for (; I != end; ++I) {
661 if (buf != buf->masterBuffer())
670 The SIGHUP signal does not exist on Windows and does not need to be handled.
672 Windows handles SIGFPE and SIGSEGV signals as expected.
674 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
675 cause a new thread to be spawned. This may well result in unexpected
676 behaviour by the single-threaded LyX.
678 SIGTERM signals will come only from another process actually sending
679 that signal using 'raise' in Windows' POSIX compatability layer. It will
680 not come from the general "terminate process" methods that everyone
681 actually uses (and which can't be trapped). Killing an app 'politely' on
682 Windows involves first sending a WM_CLOSE message, something that is
683 caught already by the Qt frontend.
685 For more information see:
687 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
688 ...signals are mostly useless on Windows for a variety of reasons that are
691 'UNIX Application Migration Guide, Chapter 9'
692 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
694 'How To Terminate an Application "Cleanly" in Win32'
695 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
699 static void error_handler(int err_sig)
701 // Throw away any signals other than the first one received.
702 static sig_atomic_t handling_error = false;
705 handling_error = true;
707 // We have received a signal indicating a fatal error, so
708 // try and save the data ASAP.
709 LyX::cref().emergencyCleanup();
711 // These lyxerr calls may or may not work:
713 // Signals are asynchronous, so the main program may be in a very
714 // fragile state when a signal is processed and thus while a signal
715 // handler function executes.
716 // In general, therefore, we should avoid performing any
717 // I/O operations or calling most library and system functions from
720 // This shouldn't matter here, however, as we've already invoked
725 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
729 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
732 lyxerr << "\nlyx: SIGSEGV signal caught\n"
733 "Sorry, you have found a bug in LyX. "
734 "Please read the bug-reporting instructions "
735 "in Help->Introduction and send us a bug report, "
736 "if necessary. Thanks !\nBye." << endl;
744 // Deinstall the signal handlers
746 signal(SIGHUP, SIG_DFL);
748 signal(SIGINT, SIG_DFL);
749 signal(SIGFPE, SIG_DFL);
750 signal(SIGSEGV, SIG_DFL);
751 signal(SIGTERM, SIG_DFL);
754 if (err_sig == SIGSEGV ||
755 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
757 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
766 void LyX::printError(ErrorItem const & ei)
768 docstring tmp = _("LyX: ") + ei.error + char_type(':')
770 std::cerr << to_utf8(tmp) << std::endl;
777 signal(SIGHUP, error_handler);
779 signal(SIGFPE, error_handler);
780 signal(SIGSEGV, error_handler);
781 signal(SIGINT, error_handler);
782 signal(SIGTERM, error_handler);
783 // SIGPIPE can be safely ignored.
785 lyxrc.tempdir_path = package().temp_dir().absFilename();
786 lyxrc.document_path = package().document_dir().absFilename();
788 if (lyxrc.example_path.empty()) {
789 lyxrc.example_path = addPath(package().system_support().absFilename(),
792 if (lyxrc.template_path.empty()) {
793 lyxrc.template_path = addPath(package().system_support().absFilename(),
798 // Read configuration files
801 // This one may have been distributed along with LyX.
802 if (!readRcFile("lyxrc.dist"))
805 // Set the language defined by the distributor.
806 //setGuiLanguage(lyxrc.gui_language);
808 // Set the PATH correctly.
809 #if !defined (USE_POSIX_PACKAGING)
810 // Add the directory containing the LyX executable to the path
811 // so that LyX can find things like tex2lyx.
812 if (package().build_support().empty())
813 prependEnvPath("PATH", package().binary_dir().absFilename());
815 if (!lyxrc.path_prefix.empty())
816 prependEnvPath("PATH", lyxrc.path_prefix);
818 // Check that user LyX directory is ok.
819 if (queryUserLyXDir(package().explicit_user_support()))
820 reconfigureUserLyXDir();
822 // no need for a splash when there is no GUI
827 // This one is generated in user_support directory by lib/configure.py.
828 if (!readRcFile("lyxrc.defaults"))
831 // Query the OS to know what formats are viewed natively
832 formats.setAutoOpen();
834 // Read lyxrc.dist again to be able to override viewer auto-detection.
835 readRcFile("lyxrc.dist");
837 system_lyxrc = lyxrc;
838 system_formats = formats;
839 pimpl_->system_converters_ = pimpl_->converters_;
840 pimpl_->system_movers_ = pimpl_->movers_;
841 system_lcolor = lcolor;
843 // This one is edited through the preferences dialog.
844 if (!readRcFile("preferences"))
847 if (!readEncodingsFile("encodings", "unicodesymbols"))
849 if (!readLanguagesFile("languages"))
853 LYXERR(Debug::INIT, "Reading layouts...");
859 // read keymap and ui files in batch mode as well
860 // because InsetInfo needs to know these to produce
861 // the correct output
863 // Set the language defined by the user.
864 //setGuiLanguage(lyxrc.gui_language);
866 // Set up command definitions
867 pimpl_->toplevel_cmddef_.reset(new CmdDef);
868 pimpl_->toplevel_cmddef_->read(lyxrc.def_file);
871 pimpl_->toplevel_keymap_.reset(new KeyMap);
872 pimpl_->toplevel_keymap_->read("site");
873 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
874 // load user bind file user.bind
875 pimpl_->toplevel_keymap_->read("user");
877 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
880 if (!readUIFile(lyxrc.ui_file))
883 if (lyxerr.debugging(Debug::LYXRC))
886 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
887 if (!lyxrc.path_prefix.empty())
888 prependEnvPath("PATH", lyxrc.path_prefix);
890 FileName const document_path(lyxrc.document_path);
891 if (document_path.exists() && document_path.isDirectory())
892 package().document_dir() = document_path;
894 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
895 if (package().temp_dir().empty()) {
896 Alert::error(_("Could not create temporary directory"),
897 bformat(_("Could not create a temporary directory in\n"
898 "%1$s. Make sure that this\n"
899 "path exists and is writable and try again."),
900 from_utf8(lyxrc.tempdir_path)));
901 // createLyXTmpDir() tries sufficiently hard to create a
902 // usable temp dir, so the probability to come here is
903 // close to zero. We therefore don't try to overcome this
904 // problem with e.g. asking the user for a new path and
905 // trying again but simply exit.
909 LYXERR(Debug::INIT, "LyX tmp dir: `"
910 << package().temp_dir().absFilename() << '\'');
912 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
913 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
915 // This must happen after package initialization and after lyxrc is
916 // read, therefore it can't be done by a static object.
917 ConverterCache::init();
923 void LyX::emergencyCleanup() const
925 // what to do about tmpfiles is non-obvious. we would
926 // like to delete any we find, but our lyxdir might
927 // contain documents etc. which might be helpful on
930 pimpl_->buffer_list_.emergencyWriteAll();
932 if (pimpl_->lyx_server_)
933 pimpl_->lyx_server_->emergencyCleanup();
934 pimpl_->lyx_server_.reset();
935 pimpl_->lyx_socket_.reset();
940 void LyX::deadKeyBindings(KeyMap * kbmap)
942 // bindKeyings for transparent handling of deadkeys
943 // The keysyms are gotten from XFree86 X11R6
944 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
945 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
946 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
947 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
948 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
949 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
950 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
951 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
952 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
953 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
954 // nothing with this name
955 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
956 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
957 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
958 // nothing with this name either...
959 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
960 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
961 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
962 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
966 // return true if file does not exist or is older than configure.py.
967 static bool needsUpdate(string const & file)
969 // We cannot initialize configure_script directly because the package
970 // is not initialized yet when static objects are constructed.
971 static FileName configure_script;
972 static bool firstrun = true;
975 FileName(addName(package().system_support().absFilename(),
981 FileName(addName(package().user_support().absFilename(), file));
982 return !absfile.exists()
983 || configure_script.lastModified() > absfile.lastModified();
987 bool LyX::queryUserLyXDir(bool explicit_userdir)
989 // Does user directory exist?
990 FileName const sup = package().user_support();
991 if (sup.exists() && sup.isDirectory()) {
994 return needsUpdate("lyxrc.defaults")
995 || needsUpdate("lyxmodules.lst")
996 || needsUpdate("textclass.lst")
997 || needsUpdate("packages.lst");
1000 first_start = !explicit_userdir;
1002 // If the user specified explicitly a directory, ask whether
1003 // to create it. If the user says "no", then exit.
1004 if (explicit_userdir &&
1006 _("Missing user LyX directory"),
1007 bformat(_("You have specified a non-existent user "
1008 "LyX directory, %1$s.\n"
1009 "It is needed to keep your own configuration."),
1010 from_utf8(package().user_support().absFilename())),
1012 _("&Create directory"),
1014 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1015 earlyExit(EXIT_FAILURE);
1018 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1019 from_utf8(sup.absFilename()))) << endl;
1021 if (!sup.createDirectory(0755)) {
1022 // Failed, so let's exit.
1023 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1025 earlyExit(EXIT_FAILURE);
1032 bool LyX::readRcFile(string const & name)
1034 LYXERR(Debug::INIT, "About to read " << name << "... ");
1036 FileName const lyxrc_path = libFileSearch(string(), name);
1037 if (!lyxrc_path.empty()) {
1038 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1039 if (lyxrc.read(lyxrc_path) < 0) {
1040 showFileError(name);
1044 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1050 // Read the ui file `name'
1051 bool LyX::readUIFile(string const & name, bool include)
1061 struct keyword_item uitags[ui_last - 1] = {
1062 { "include", ui_include },
1063 { "menuset", ui_menuset },
1064 { "toolbars", ui_toolbars },
1065 { "toolbarset", ui_toolbarset }
1068 // Ensure that a file is read only once (prevents include loops)
1069 static std::list<string> uifiles;
1070 std::list<string>::const_iterator it = uifiles.begin();
1071 std::list<string>::const_iterator end = uifiles.end();
1072 it = std::find(it, end, name);
1074 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1075 << "Is this an include loop?");
1079 LYXERR(Debug::INIT, "About to read " << name << "...");
1084 ui_path = libFileSearch("ui", name, "inc");
1085 if (ui_path.empty())
1086 ui_path = libFileSearch("ui",
1087 changeExtension(name, "inc"));
1090 ui_path = libFileSearch("ui", name, "ui");
1092 if (ui_path.empty()) {
1093 LYXERR(Debug::INIT, "Could not find " << name);
1094 showFileError(name);
1098 uifiles.push_back(name);
1100 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1101 Lexer lex(uitags, ui_last - 1);
1102 lex.setFile(ui_path);
1104 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1108 if (lyxerr.debugging(Debug::PARSER))
1109 lex.printTable(lyxerr);
1111 while (lex.isOK()) {
1112 switch (lex.lex()) {
1115 string const file = lex.getString();
1116 if (!readUIFile(file, true))
1121 menubackend.read(lex);
1125 toolbarbackend.readToolbars(lex);
1129 toolbarbackend.readToolbarSettings(lex);
1133 if (!rtrim(lex.getString()).empty())
1134 lex.printError("LyX::ReadUIFile: "
1135 "Unknown menu tag: `$$Token'");
1143 // Read the languages file `name'
1144 bool LyX::readLanguagesFile(string const & name)
1146 LYXERR(Debug::INIT, "About to read " << name << "...");
1148 FileName const lang_path = libFileSearch(string(), name);
1149 if (lang_path.empty()) {
1150 showFileError(name);
1153 languages.read(lang_path);
1158 // Read the encodings file `name'
1159 bool LyX::readEncodingsFile(string const & enc_name,
1160 string const & symbols_name)
1162 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1163 << symbols_name << "...");
1165 FileName const symbols_path = libFileSearch(string(), symbols_name);
1166 if (symbols_path.empty()) {
1167 showFileError(symbols_name);
1171 FileName const enc_path = libFileSearch(string(), enc_name);
1172 if (enc_path.empty()) {
1173 showFileError(enc_name);
1176 encodings.read(enc_path, symbols_path);
1185 /// return the the number of arguments consumed
1186 typedef boost::function<int(string const &, string const &)> cmd_helper;
1188 int parse_dbg(string const & arg, string const &)
1191 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1192 Debug::showTags(lyxerr);
1195 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1197 lyxerr.level(Debug::value(arg));
1198 Debug::showLevel(lyxerr, lyxerr.level());
1203 int parse_help(string const &, string const &)
1206 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1207 "Command line switches (case sensitive):\n"
1208 "\t-help summarize LyX usage\n"
1209 "\t-userdir dir set user directory to dir\n"
1210 "\t-sysdir dir set system directory to dir\n"
1211 "\t-geometry WxH+X+Y set geometry of the main window\n"
1212 "\t-dbg feature[,feature]...\n"
1213 " select the features to debug.\n"
1214 " Type `lyx -dbg' to see the list of features\n"
1215 "\t-x [--execute] command\n"
1216 " where command is a lyx command.\n"
1217 "\t-e [--export] fmt\n"
1218 " where fmt is the export format of choice.\n"
1219 " Look on Tools->Preferences->File formats->Format\n"
1220 " to get an idea which parameters should be passed.\n"
1221 "\t-i [--import] fmt file.xxx\n"
1222 " where fmt is the import format of choice\n"
1223 " and file.xxx is the file to be imported.\n"
1224 "\t-version summarize version and build info\n"
1225 "Check the LyX man page for more details.")) << endl;
1231 int parse_version(string const &, string const &)
1233 lyxerr << "LyX " << lyx_version
1234 << " (" << lyx_release_date << ")" << endl;
1235 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1237 lyxerr << lyx_version_info << endl;
1243 int parse_sysdir(string const & arg, string const &)
1246 Alert::error(_("No system directory"),
1247 _("Missing directory for -sysdir switch"));
1250 cl_system_support = arg;
1255 int parse_userdir(string const & arg, string const &)
1258 Alert::error(_("No user directory"),
1259 _("Missing directory for -userdir switch"));
1262 cl_user_support = arg;
1267 int parse_execute(string const & arg, string const &)
1270 Alert::error(_("Incomplete command"),
1271 _("Missing command string after --execute switch"));
1279 int parse_export(string const & type, string const &)
1282 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1283 "--export switch")) << endl;
1286 batch = "buffer-export " + type;
1292 int parse_import(string const & type, string const & file)
1295 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1296 "--import switch")) << endl;
1300 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1304 batch = "buffer-import " + type + ' ' + file;
1309 int parse_geometry(string const & arg1, string const &)
1312 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1313 // remove also the arg
1316 // don't remove "-geometry"
1325 void LyX::easyParse(int & argc, char * argv[])
1327 std::map<string, cmd_helper> cmdmap;
1329 cmdmap["-dbg"] = parse_dbg;
1330 cmdmap["-help"] = parse_help;
1331 cmdmap["--help"] = parse_help;
1332 cmdmap["-version"] = parse_version;
1333 cmdmap["--version"] = parse_version;
1334 cmdmap["-sysdir"] = parse_sysdir;
1335 cmdmap["-userdir"] = parse_userdir;
1336 cmdmap["-x"] = parse_execute;
1337 cmdmap["--execute"] = parse_execute;
1338 cmdmap["-e"] = parse_export;
1339 cmdmap["--export"] = parse_export;
1340 cmdmap["-i"] = parse_import;
1341 cmdmap["--import"] = parse_import;
1342 cmdmap["-geometry"] = parse_geometry;
1344 for (int i = 1; i < argc; ++i) {
1345 std::map<string, cmd_helper>::const_iterator it
1346 = cmdmap.find(argv[i]);
1348 // don't complain if not found - may be parsed later
1349 if (it == cmdmap.end())
1353 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1355 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1357 int const remove = 1 + it->second(arg, arg2);
1359 // Now, remove used arguments by shifting
1360 // the following ones remove places down.
1363 for (int j = i; j < argc; ++j)
1364 argv[j] = argv[j + remove];
1369 pimpl_->batch_command = batch;
1373 FuncStatus getStatus(FuncRequest const & action)
1375 return LyX::ref().lyxFunc().getStatus(action);
1379 void dispatch(FuncRequest const & action)
1381 LyX::ref().lyxFunc().dispatch(action);
1385 BufferList & theBufferList()
1387 return LyX::ref().bufferList();
1391 LyXFunc & theLyXFunc()
1393 return LyX::ref().lyxFunc();
1397 Server & theServer()
1399 // FIXME: this should not be use_gui dependent
1400 BOOST_ASSERT(use_gui);
1401 return LyX::ref().server();
1405 ServerSocket & theServerSocket()
1407 // FIXME: this should not be use_gui dependent
1408 BOOST_ASSERT(use_gui);
1409 return LyX::ref().socket();
1413 KeyMap & theTopLevelKeymap()
1415 return LyX::ref().topLevelKeymap();
1419 Converters & theConverters()
1421 return LyX::ref().converters();
1425 Converters & theSystemConverters()
1427 return LyX::ref().systemConverters();
1431 Movers & theMovers()
1433 return LyX::ref().pimpl_->movers_;
1437 Mover const & getMover(std::string const & fmt)
1439 return LyX::ref().pimpl_->movers_(fmt);
1443 void setMover(std::string const & fmt, std::string const & command)
1445 LyX::ref().pimpl_->movers_.set(fmt, command);
1449 Movers & theSystemMovers()
1451 return LyX::ref().pimpl_->system_movers_;
1455 Messages & getMessages(std::string const & language)
1457 return LyX::ref().getMessages(language);
1461 Messages & getGuiMessages()
1463 return LyX::ref().getGuiMessages();