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"
51 #include "frontends/LyXView.h"
53 #include "support/environment.h"
54 #include "support/filetools.h"
55 #include "support/lstrings.h"
56 #include "support/lyxlib.h"
57 #include "support/ExceptionMessage.h"
58 #include "support/os.h"
59 #include "support/Package.h"
60 #include "support/Path.h"
61 #include "support/Systemcall.h"
63 #include <boost/bind.hpp>
64 #include <boost/scoped_ptr.hpp>
80 #ifndef CXX_GLOBAL_CSTD
88 using support::addName;
89 using support::addPath;
90 using support::bformat;
91 using support::changeExtension;
92 using support::createLyXTmpDir;
93 using support::FileName;
94 using support::fileSearch;
95 using support::getEnv;
96 using support::i18nLibFileSearch;
97 using support::libFileSearch;
98 using support::package;
99 using support::prependEnvPath;
100 using support::rtrim;
101 using support::Systemcall;
102 using frontend::LyXView;
104 namespace Alert = frontend::Alert;
105 namespace os = support::os;
109 // Are we using the GUI at all? We default to true and this is changed
110 // to false when the export feature is used.
114 bool quitting; // flag, that we are quitting the program
118 // Filled with the command line arguments "foo" of "-sysdir foo" or
120 string cl_system_support;
121 string cl_user_support;
123 std::string geometryArg;
125 LyX * singleton_ = 0;
127 void showFileError(string const & error)
129 Alert::warning(_("Could not read configuration file"),
130 bformat(_("Error while reading the configuration file\n%1$s.\n"
131 "Please check your installation."), from_utf8(error)));
135 void reconfigureUserLyXDir()
137 string const configure_command = package().configure_command();
139 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
140 support::PathChanger p(package().user_support());
142 one.startscript(Systemcall::Wait, configure_command);
143 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
149 /// The main application class private implementation.
154 // Set the default User Interface language as soon as possible.
155 // The language used will be derived from the environment
157 messages_["GUI"] = Messages();
159 /// our function handler
162 BufferList buffer_list_;
164 boost::scoped_ptr<KeyMap> toplevel_keymap_;
166 boost::scoped_ptr<CmdDef> toplevel_cmddef_;
168 boost::scoped_ptr<Server> lyx_server_;
170 boost::scoped_ptr<ServerSocket> lyx_socket_;
172 boost::scoped_ptr<frontend::Application> application_;
173 /// lyx session, containing lastfiles, lastfilepos, and lastopened
174 boost::scoped_ptr<Session> session_;
176 /// Files to load at start.
177 vector<FileName> files_to_load_;
179 /// The messages translators.
180 map<string, Messages> messages_;
182 /// The file converters.
183 Converters converters_;
185 // The system converters copy after reading lyxrc.defaults.
186 Converters system_converters_;
191 Movers system_movers_;
193 /// has this user started lyx for the first time?
195 /// the parsed command line batch command if any
196 std::string batch_command;
200 frontend::Application * theApp()
203 return singleton_->pimpl_->application_.get();
217 BOOST_ASSERT(singleton_);
222 LyX const & LyX::cref()
224 BOOST_ASSERT(singleton_);
237 BufferList & LyX::bufferList()
239 return pimpl_->buffer_list_;
243 BufferList const & LyX::bufferList() const
245 return pimpl_->buffer_list_;
249 Session & LyX::session()
251 BOOST_ASSERT(pimpl_->session_.get());
252 return *pimpl_->session_.get();
256 Session const & LyX::session() const
258 BOOST_ASSERT(pimpl_->session_.get());
259 return *pimpl_->session_.get();
263 LyXFunc & LyX::lyxFunc()
265 return pimpl_->lyxfunc_;
269 LyXFunc const & LyX::lyxFunc() const
271 return pimpl_->lyxfunc_;
275 Server & LyX::server()
277 BOOST_ASSERT(pimpl_->lyx_server_.get());
278 return *pimpl_->lyx_server_.get();
282 Server const & LyX::server() const
284 BOOST_ASSERT(pimpl_->lyx_server_.get());
285 return *pimpl_->lyx_server_.get();
289 ServerSocket & LyX::socket()
291 BOOST_ASSERT(pimpl_->lyx_socket_.get());
292 return *pimpl_->lyx_socket_.get();
296 ServerSocket const & LyX::socket() const
298 BOOST_ASSERT(pimpl_->lyx_socket_.get());
299 return *pimpl_->lyx_socket_.get();
303 frontend::Application & LyX::application()
305 BOOST_ASSERT(pimpl_->application_.get());
306 return *pimpl_->application_.get();
310 frontend::Application const & LyX::application() const
312 BOOST_ASSERT(pimpl_->application_.get());
313 return *pimpl_->application_.get();
317 KeyMap & LyX::topLevelKeymap()
319 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
320 return *pimpl_->toplevel_keymap_.get();
324 CmdDef & LyX::topLevelCmdDef()
326 BOOST_ASSERT(pimpl_->toplevel_cmddef_.get());
327 return *pimpl_->toplevel_cmddef_.get();
331 Converters & LyX::converters()
333 return pimpl_->converters_;
337 Converters & LyX::systemConverters()
339 return pimpl_->system_converters_;
343 KeyMap const & LyX::topLevelKeymap() const
345 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
346 return *pimpl_->toplevel_keymap_.get();
350 Messages & LyX::getMessages(std::string const & language)
352 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
354 if (it != pimpl_->messages_.end())
357 std::pair<map<string, Messages>::iterator, bool> result =
358 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
360 BOOST_ASSERT(result.second);
361 return result.first->second;
365 Messages & LyX::getGuiMessages()
367 return pimpl_->messages_["GUI"];
371 void LyX::setGuiLanguage(std::string const & language)
373 pimpl_->messages_["GUI"] = Messages(language);
377 int LyX::exec(int & argc, char * argv[])
379 // Here we need to parse the command line. At least
380 // we need to parse for "-dbg" and "-help"
381 easyParse(argc, argv);
384 support::init_package(to_utf8(from_local8bit(argv[0])),
385 cl_system_support, cl_user_support,
386 support::top_build_dir_is_one_level_up);
387 } catch (support::ExceptionMessage const & message) {
388 if (message.type_ == support::ErrorException) {
389 Alert::error(message.title_, message.details_);
391 } else if (message.type_ == support::WarningException) {
392 Alert::warning(message.title_, message.details_);
396 // Reinit the messages machinery in case package() knows
397 // something interesting about the locale directory.
401 // FIXME: create a ConsoleApplication
402 int exit_status = init(argc, argv);
410 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
415 BufferList::iterator begin = pimpl_->buffer_list_.begin();
417 bool final_success = false;
418 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
420 if (buf != buf->masterBuffer())
422 bool success = false;
423 buf->dispatch(pimpl_->batch_command, &success);
424 final_success |= success;
427 return !final_success;
430 // Let the frontend parse and remove all arguments that it knows
431 pimpl_->application_.reset(createApplication(argc, argv));
435 // Parse and remove all known arguments in the LyX singleton
436 // Give an error for all remaining ones.
437 int exit_status = init(argc, argv);
439 // Kill the application object before exiting.
440 pimpl_->application_.reset();
447 /* Create a CoreApplication class that will provide the main event loop
448 * and the socket callback registering. With Qt4, only QtCore
449 * library would be needed.
450 * When this is done, a server_mode could be created and the following two
451 * line would be moved out from here.
453 // Note: socket callback must be registered after init(argc, argv)
454 // such that package().temp_dir() is properly initialized.
455 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
456 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
457 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
459 // Start the real execution loop.
460 exit_status = pimpl_->application_->exec();
468 void LyX::prepareExit()
470 // Clear the clipboard and selection stack:
471 cap::clearCutStack();
472 cap::clearSelection();
474 // Set a flag that we do quitting from the program,
475 // so no refreshes are necessary.
478 // close buffers first
479 pimpl_->buffer_list_.closeAll();
481 // do any other cleanup procedures now
482 if (package().temp_dir() != package().system_temp_dir()) {
483 LYXERR(Debug::INFO, "Deleting tmp dir "
484 << package().temp_dir().absFilename());
486 if (!package().temp_dir().destroyDirectory()) {
487 docstring const msg =
488 bformat(_("Unable to remove the temporary directory %1$s"),
489 from_utf8(package().temp_dir().absFilename()));
490 Alert::warning(_("Unable to remove temporary directory"), msg);
495 if (pimpl_->session_)
496 pimpl_->session_->writeFile();
497 pimpl_->session_.reset();
498 pimpl_->lyx_server_.reset();
499 pimpl_->lyx_socket_.reset();
502 // Kill the application object before exiting. This avoids crashes
503 // when exiting on Linux.
504 if (pimpl_->application_)
505 pimpl_->application_.reset();
509 void LyX::earlyExit(int status)
511 BOOST_ASSERT(pimpl_->application_.get());
512 // LyX::pimpl_::application_ is not initialised at this
513 // point so it's safe to just exit after some cleanup.
519 int LyX::init(int & argc, char * argv[])
521 // check for any spurious extra arguments
522 // other than documents
523 for (int argi = 1; argi < argc ; ++argi) {
524 if (argv[argi][0] == '-') {
526 bformat(_("Wrong command line option `%1$s'. Exiting."),
527 from_utf8(argv[argi]))) << endl;
532 // Initialization of LyX (reads lyxrc and more)
533 LYXERR(Debug::INIT, "Initializing LyX::init...");
534 bool success = init();
535 LYXERR(Debug::INIT, "Initializing LyX::init...done");
539 for (int argi = argc - 1; argi >= 1; --argi) {
540 // get absolute path of file and add ".lyx" to
541 // the filename if necessary
542 pimpl_->files_to_load_.push_back(fileSearch(string(),
543 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
544 "lyx", support::allow_unreadable));
548 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
554 void LyX::addFileToLoad(FileName const & fname)
556 vector<FileName>::const_iterator cit = std::find(
557 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
560 if (cit == pimpl_->files_to_load_.end())
561 pimpl_->files_to_load_.push_back(fname);
565 void LyX::loadFiles()
567 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
568 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
570 for (; it != end; ++it) {
574 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
575 if (buf->loadLyXFile(*it)) {
576 ErrorList const & el = buf->errorList("Parse");
578 for_each(el.begin(), el.end(),
579 boost::bind(&LyX::printError, this, _1));
582 pimpl_->buffer_list_.release(buf);
587 void LyX::execBatchCommands()
589 // The advantage of doing this here is that the event loop
590 // is already started. So any need for interaction will be
594 // if reconfiguration is needed.
595 if (textclasslist.empty()) {
596 switch (Alert::prompt(
597 _("No textclass is found"),
598 _("LyX cannot continue because no textclass is found. "
599 "You can either reconfigure normally, or reconfigure using "
600 "default textclasses, or quit LyX."),
607 // regular reconfigure
608 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
611 // reconfigure --without-latex-config
612 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
613 " --without-latex-config"));
616 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
620 // Execute batch commands if available
621 if (pimpl_->batch_command.empty())
624 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
626 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
630 void LyX::restoreGuiSession()
632 LyXView * view = newLyXView();
634 // if there is no valid class list, do not load any file.
635 if (textclasslist.empty())
638 // if some files were specified at command-line we assume that the
639 // user wants to edit *these* files and not to restore the session.
640 if (!pimpl_->files_to_load_.empty()) {
641 for_each(pimpl_->files_to_load_.begin(),
642 pimpl_->files_to_load_.end(),
643 bind(&LyXView::loadLyXFile, view, _1, true));
644 // clear this list to save a few bytes of RAM
645 pimpl_->files_to_load_.clear();
646 pimpl_->session_->lastOpened().clear();
648 } else if (lyxrc.load_session) {
649 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
650 // do not add to the lastfile list since these files are restored from
651 // last session, and should be already there (regular files), or should
652 // not be added at all (help files).
653 for_each(lastopened.begin(), lastopened.end(),
654 bind(&LyXView::loadLyXFile, view, _1, false));
656 // clear this list to save a few bytes of RAM
657 pimpl_->session_->lastOpened().clear();
660 BufferList::iterator I = pimpl_->buffer_list_.begin();
661 BufferList::iterator end = pimpl_->buffer_list_.end();
662 for (; I != end; ++I) {
664 if (buf != buf->masterBuffer())
669 // FIXME: Switch to the last loaded Buffer. This must not be the first one
670 // because the Buffer won't be connected in this case. The correct solution
671 // would be to avoid the manual connection of the current Buffer in LyXView.
672 if (!pimpl_->buffer_list_.empty())
673 view->setBuffer(pimpl_->buffer_list_.last());
677 LyXView * LyX::newLyXView()
682 // create the main window
683 LyXView * view = &pimpl_->application_->createView(geometryArg);
691 The SIGHUP signal does not exist on Windows and does not need to be handled.
693 Windows handles SIGFPE and SIGSEGV signals as expected.
695 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
696 cause a new thread to be spawned. This may well result in unexpected
697 behaviour by the single-threaded LyX.
699 SIGTERM signals will come only from another process actually sending
700 that signal using 'raise' in Windows' POSIX compatability layer. It will
701 not come from the general "terminate process" methods that everyone
702 actually uses (and which can't be trapped). Killing an app 'politely' on
703 Windows involves first sending a WM_CLOSE message, something that is
704 caught already by the Qt frontend.
706 For more information see:
708 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
709 ...signals are mostly useless on Windows for a variety of reasons that are
712 'UNIX Application Migration Guide, Chapter 9'
713 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
715 'How To Terminate an Application "Cleanly" in Win32'
716 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
720 static void error_handler(int err_sig)
722 // Throw away any signals other than the first one received.
723 static sig_atomic_t handling_error = false;
726 handling_error = true;
728 // We have received a signal indicating a fatal error, so
729 // try and save the data ASAP.
730 LyX::cref().emergencyCleanup();
732 // These lyxerr calls may or may not work:
734 // Signals are asynchronous, so the main program may be in a very
735 // fragile state when a signal is processed and thus while a signal
736 // handler function executes.
737 // In general, therefore, we should avoid performing any
738 // I/O operations or calling most library and system functions from
741 // This shouldn't matter here, however, as we've already invoked
746 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
750 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
753 lyxerr << "\nlyx: SIGSEGV signal caught\n"
754 "Sorry, you have found a bug in LyX. "
755 "Please read the bug-reporting instructions "
756 "in Help->Introduction and send us a bug report, "
757 "if necessary. Thanks !\nBye." << endl;
765 // Deinstall the signal handlers
767 signal(SIGHUP, SIG_DFL);
769 signal(SIGINT, SIG_DFL);
770 signal(SIGFPE, SIG_DFL);
771 signal(SIGSEGV, SIG_DFL);
772 signal(SIGTERM, SIG_DFL);
775 if (err_sig == SIGSEGV ||
776 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
778 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
787 void LyX::printError(ErrorItem const & ei)
789 docstring tmp = _("LyX: ") + ei.error + char_type(':')
791 std::cerr << to_utf8(tmp) << std::endl;
795 void LyX::initGuiFont()
797 if (lyxrc.roman_font_name.empty())
798 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
800 if (lyxrc.sans_font_name.empty())
801 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
803 if (lyxrc.typewriter_font_name.empty())
804 lyxrc.typewriter_font_name
805 = pimpl_->application_->typewriterFontName();
812 signal(SIGHUP, error_handler);
814 signal(SIGFPE, error_handler);
815 signal(SIGSEGV, error_handler);
816 signal(SIGINT, error_handler);
817 signal(SIGTERM, error_handler);
818 // SIGPIPE can be safely ignored.
820 lyxrc.tempdir_path = package().temp_dir().absFilename();
821 lyxrc.document_path = package().document_dir().absFilename();
823 if (lyxrc.template_path.empty()) {
824 lyxrc.template_path = addPath(package().system_support().absFilename(),
829 // Read configuration files
832 // This one may have been distributed along with LyX.
833 if (!readRcFile("lyxrc.dist"))
836 // Set the language defined by the distributor.
837 //setGuiLanguage(lyxrc.gui_language);
839 // Set the PATH correctly.
840 #if !defined (USE_POSIX_PACKAGING)
841 // Add the directory containing the LyX executable to the path
842 // so that LyX can find things like tex2lyx.
843 if (package().build_support().empty())
844 prependEnvPath("PATH", package().binary_dir().absFilename());
846 if (!lyxrc.path_prefix.empty())
847 prependEnvPath("PATH", lyxrc.path_prefix);
849 // Check that user LyX directory is ok.
850 if (queryUserLyXDir(package().explicit_user_support()))
851 reconfigureUserLyXDir();
853 // no need for a splash when there is no GUI
858 // This one is generated in user_support directory by lib/configure.py.
859 if (!readRcFile("lyxrc.defaults"))
862 // Query the OS to know what formats are viewed natively
863 formats.setAutoOpen();
865 // Read lyxrc.dist again to be able to override viewer auto-detection.
866 readRcFile("lyxrc.dist");
868 system_lyxrc = lyxrc;
869 system_formats = formats;
870 pimpl_->system_converters_ = pimpl_->converters_;
871 pimpl_->system_movers_ = pimpl_->movers_;
872 system_lcolor = lcolor;
874 // This one is edited through the preferences dialog.
875 if (!readRcFile("preferences"))
878 if (!readEncodingsFile("encodings", "unicodesymbols"))
880 if (!readLanguagesFile("languages"))
884 LYXERR(Debug::INIT, "Reading layouts...");
890 // read keymap and ui files in batch mode as well
891 // because InsetInfo needs to know these to produce
892 // the correct output
894 // Set the language defined by the user.
895 //setGuiLanguage(lyxrc.gui_language);
897 // Set up command definitions
898 pimpl_->toplevel_cmddef_.reset(new CmdDef);
899 pimpl_->toplevel_cmddef_->read(lyxrc.def_file);
902 pimpl_->toplevel_keymap_.reset(new KeyMap);
903 pimpl_->toplevel_keymap_->read("site");
904 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
905 // load user bind file user.bind
906 pimpl_->toplevel_keymap_->read("user");
908 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
911 if (!readUIFile(lyxrc.ui_file))
914 if (lyxerr.debugging(Debug::LYXRC))
917 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
918 if (!lyxrc.path_prefix.empty())
919 prependEnvPath("PATH", lyxrc.path_prefix);
921 FileName const document_path(lyxrc.document_path);
922 if (document_path.exists() && document_path.isDirectory())
923 package().document_dir() = document_path;
925 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
926 if (package().temp_dir().empty()) {
927 Alert::error(_("Could not create temporary directory"),
928 bformat(_("Could not create a temporary directory in\n"
929 "%1$s. Make sure that this\n"
930 "path exists and is writable and try again."),
931 from_utf8(lyxrc.tempdir_path)));
932 // createLyXTmpDir() tries sufficiently hard to create a
933 // usable temp dir, so the probability to come here is
934 // close to zero. We therefore don't try to overcome this
935 // problem with e.g. asking the user for a new path and
936 // trying again but simply exit.
940 LYXERR(Debug::INIT, "LyX tmp dir: `"
941 << package().temp_dir().absFilename() << '\'');
943 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
944 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
946 // This must happen after package initialization and after lyxrc is
947 // read, therefore it can't be done by a static object.
948 ConverterCache::init();
954 void LyX::emergencyCleanup() const
956 // what to do about tmpfiles is non-obvious. we would
957 // like to delete any we find, but our lyxdir might
958 // contain documents etc. which might be helpful on
961 pimpl_->buffer_list_.emergencyWriteAll();
963 if (pimpl_->lyx_server_)
964 pimpl_->lyx_server_->emergencyCleanup();
965 pimpl_->lyx_server_.reset();
966 pimpl_->lyx_socket_.reset();
971 void LyX::deadKeyBindings(KeyMap * kbmap)
973 // bindKeyings for transparent handling of deadkeys
974 // The keysyms are gotten from XFree86 X11R6
975 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
976 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
977 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
978 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
979 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
980 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
981 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
982 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
983 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
984 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
985 // nothing with this name
986 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
987 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
988 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
989 // nothing with this name either...
990 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
991 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
992 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
993 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
997 // return true if file does not exist or is older than configure.py.
998 static bool needsUpdate(string const & file)
1000 // We cannot initialize configure_script directly because the package
1001 // is not initialized yet when static objects are constructed.
1002 static FileName configure_script;
1003 static bool firstrun = true;
1006 FileName(addName(package().system_support().absFilename(),
1012 FileName(addName(package().user_support().absFilename(), file));
1013 return !absfile.exists()
1014 || configure_script.lastModified() > absfile.lastModified();
1018 bool LyX::queryUserLyXDir(bool explicit_userdir)
1020 // Does user directory exist?
1021 FileName const sup = package().user_support();
1022 if (sup.exists() && sup.isDirectory()) {
1023 first_start = false;
1025 return needsUpdate("lyxrc.defaults")
1026 || needsUpdate("lyxmodules.lst")
1027 || needsUpdate("textclass.lst")
1028 || needsUpdate("packages.lst");
1031 first_start = !explicit_userdir;
1033 // If the user specified explicitly a directory, ask whether
1034 // to create it. If the user says "no", then exit.
1035 if (explicit_userdir &&
1037 _("Missing user LyX directory"),
1038 bformat(_("You have specified a non-existent user "
1039 "LyX directory, %1$s.\n"
1040 "It is needed to keep your own configuration."),
1041 from_utf8(package().user_support().absFilename())),
1043 _("&Create directory"),
1045 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1046 earlyExit(EXIT_FAILURE);
1049 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1050 from_utf8(sup.absFilename()))) << endl;
1052 if (!sup.createDirectory(0755)) {
1053 // Failed, so let's exit.
1054 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1056 earlyExit(EXIT_FAILURE);
1063 bool LyX::readRcFile(string const & name)
1065 LYXERR(Debug::INIT, "About to read " << name << "... ");
1067 FileName const lyxrc_path = libFileSearch(string(), name);
1068 if (!lyxrc_path.empty()) {
1069 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1070 if (lyxrc.read(lyxrc_path) < 0) {
1071 showFileError(name);
1075 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1081 // Read the ui file `name'
1082 bool LyX::readUIFile(string const & name, bool include)
1092 struct keyword_item uitags[ui_last - 1] = {
1093 { "include", ui_include },
1094 { "menuset", ui_menuset },
1095 { "toolbars", ui_toolbars },
1096 { "toolbarset", ui_toolbarset }
1099 // Ensure that a file is read only once (prevents include loops)
1100 static std::list<string> uifiles;
1101 std::list<string>::const_iterator it = uifiles.begin();
1102 std::list<string>::const_iterator end = uifiles.end();
1103 it = std::find(it, end, name);
1105 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1106 << "Is this an include loop?");
1110 LYXERR(Debug::INIT, "About to read " << name << "...");
1115 ui_path = libFileSearch("ui", name, "inc");
1116 if (ui_path.empty())
1117 ui_path = libFileSearch("ui",
1118 changeExtension(name, "inc"));
1121 ui_path = libFileSearch("ui", name, "ui");
1123 if (ui_path.empty()) {
1124 LYXERR(Debug::INIT, "Could not find " << name);
1125 showFileError(name);
1129 uifiles.push_back(name);
1131 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1132 Lexer lex(uitags, ui_last - 1);
1133 lex.setFile(ui_path);
1135 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1139 if (lyxerr.debugging(Debug::PARSER))
1140 lex.printTable(lyxerr);
1142 while (lex.isOK()) {
1143 switch (lex.lex()) {
1146 string const file = lex.getString();
1147 if (!readUIFile(file, true))
1152 menubackend.read(lex);
1156 toolbarbackend.readToolbars(lex);
1160 toolbarbackend.readToolbarSettings(lex);
1164 if (!rtrim(lex.getString()).empty())
1165 lex.printError("LyX::ReadUIFile: "
1166 "Unknown menu tag: `$$Token'");
1174 // Read the languages file `name'
1175 bool LyX::readLanguagesFile(string const & name)
1177 LYXERR(Debug::INIT, "About to read " << name << "...");
1179 FileName const lang_path = libFileSearch(string(), name);
1180 if (lang_path.empty()) {
1181 showFileError(name);
1184 languages.read(lang_path);
1189 // Read the encodings file `name'
1190 bool LyX::readEncodingsFile(string const & enc_name,
1191 string const & symbols_name)
1193 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1194 << symbols_name << "...");
1196 FileName const symbols_path = libFileSearch(string(), symbols_name);
1197 if (symbols_path.empty()) {
1198 showFileError(symbols_name);
1202 FileName const enc_path = libFileSearch(string(), enc_name);
1203 if (enc_path.empty()) {
1204 showFileError(enc_name);
1207 encodings.read(enc_path, symbols_path);
1216 /// return the the number of arguments consumed
1217 typedef boost::function<int(string const &, string const &)> cmd_helper;
1219 int parse_dbg(string const & arg, string const &)
1222 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1223 Debug::showTags(lyxerr);
1226 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1228 lyxerr.level(Debug::value(arg));
1229 Debug::showLevel(lyxerr, lyxerr.level());
1234 int parse_help(string const &, string const &)
1237 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1238 "Command line switches (case sensitive):\n"
1239 "\t-help summarize LyX usage\n"
1240 "\t-userdir dir set user directory to dir\n"
1241 "\t-sysdir dir set system directory to dir\n"
1242 "\t-geometry WxH+X+Y set geometry of the main window\n"
1243 "\t-dbg feature[,feature]...\n"
1244 " select the features to debug.\n"
1245 " Type `lyx -dbg' to see the list of features\n"
1246 "\t-x [--execute] command\n"
1247 " where command is a lyx command.\n"
1248 "\t-e [--export] fmt\n"
1249 " where fmt is the export format of choice.\n"
1250 " Look on Tools->Preferences->File formats->Format\n"
1251 " to get an idea which parameters should be passed.\n"
1252 "\t-i [--import] fmt file.xxx\n"
1253 " where fmt is the import format of choice\n"
1254 " and file.xxx is the file to be imported.\n"
1255 "\t-version summarize version and build info\n"
1256 "Check the LyX man page for more details.")) << endl;
1262 int parse_version(string const &, string const &)
1264 lyxerr << "LyX " << lyx_version
1265 << " (" << lyx_release_date << ")" << endl;
1266 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1268 lyxerr << lyx_version_info << endl;
1274 int parse_sysdir(string const & arg, string const &)
1277 Alert::error(_("No system directory"),
1278 _("Missing directory for -sysdir switch"));
1281 cl_system_support = arg;
1286 int parse_userdir(string const & arg, string const &)
1289 Alert::error(_("No user directory"),
1290 _("Missing directory for -userdir switch"));
1293 cl_user_support = arg;
1298 int parse_execute(string const & arg, string const &)
1301 Alert::error(_("Incomplete command"),
1302 _("Missing command string after --execute switch"));
1310 int parse_export(string const & type, string const &)
1313 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1314 "--export switch")) << endl;
1317 batch = "buffer-export " + type;
1323 int parse_import(string const & type, string const & file)
1326 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1327 "--import switch")) << endl;
1331 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1335 batch = "buffer-import " + type + ' ' + file;
1340 int parse_geometry(string const & arg1, string const &)
1343 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1344 // remove also the arg
1347 // don't remove "-geometry"
1356 void LyX::easyParse(int & argc, char * argv[])
1358 std::map<string, cmd_helper> cmdmap;
1360 cmdmap["-dbg"] = parse_dbg;
1361 cmdmap["-help"] = parse_help;
1362 cmdmap["--help"] = parse_help;
1363 cmdmap["-version"] = parse_version;
1364 cmdmap["--version"] = parse_version;
1365 cmdmap["-sysdir"] = parse_sysdir;
1366 cmdmap["-userdir"] = parse_userdir;
1367 cmdmap["-x"] = parse_execute;
1368 cmdmap["--execute"] = parse_execute;
1369 cmdmap["-e"] = parse_export;
1370 cmdmap["--export"] = parse_export;
1371 cmdmap["-i"] = parse_import;
1372 cmdmap["--import"] = parse_import;
1373 cmdmap["-geometry"] = parse_geometry;
1375 for (int i = 1; i < argc; ++i) {
1376 std::map<string, cmd_helper>::const_iterator it
1377 = cmdmap.find(argv[i]);
1379 // don't complain if not found - may be parsed later
1380 if (it == cmdmap.end())
1384 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1386 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1388 int const remove = 1 + it->second(arg, arg2);
1390 // Now, remove used arguments by shifting
1391 // the following ones remove places down.
1394 for (int j = i; j < argc; ++j)
1395 argv[j] = argv[j + remove];
1400 pimpl_->batch_command = batch;
1404 FuncStatus getStatus(FuncRequest const & action)
1406 return LyX::ref().lyxFunc().getStatus(action);
1410 void dispatch(FuncRequest const & action)
1412 LyX::ref().lyxFunc().dispatch(action);
1416 BufferList & theBufferList()
1418 return LyX::ref().bufferList();
1422 LyXFunc & theLyXFunc()
1424 return LyX::ref().lyxFunc();
1428 Server & theServer()
1430 // FIXME: this should not be use_gui dependent
1431 BOOST_ASSERT(use_gui);
1432 return LyX::ref().server();
1436 ServerSocket & theServerSocket()
1438 // FIXME: this should not be use_gui dependent
1439 BOOST_ASSERT(use_gui);
1440 return LyX::ref().socket();
1444 KeyMap & theTopLevelKeymap()
1446 return LyX::ref().topLevelKeymap();
1450 Converters & theConverters()
1452 return LyX::ref().converters();
1456 Converters & theSystemConverters()
1458 return LyX::ref().systemConverters();
1462 Movers & theMovers()
1464 return LyX::ref().pimpl_->movers_;
1468 Mover const & getMover(std::string const & fmt)
1470 return LyX::ref().pimpl_->movers_(fmt);
1474 void setMover(std::string const & fmt, std::string const & command)
1476 LyX::ref().pimpl_->movers_.set(fmt, command);
1480 Movers & theSystemMovers()
1482 return LyX::ref().pimpl_->system_movers_;
1486 Messages & getMessages(std::string const & language)
1488 return LyX::ref().getMessages(language);
1492 Messages & getGuiMessages()
1494 return LyX::ref().getGuiMessages();