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/Dialogs.h"
52 #include "frontends/Gui.h"
53 #include "frontends/LyXView.h"
55 #include "support/environment.h"
56 #include "support/filetools.h"
57 #include "support/lstrings.h"
58 #include "support/lyxlib.h"
59 #include "support/ExceptionMessage.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62 #include "support/Path.h"
63 #include "support/Systemcall.h"
65 #include <boost/bind.hpp>
66 #include <boost/scoped_ptr.hpp>
82 #ifndef CXX_GLOBAL_CSTD
90 using support::addName;
91 using support::addPath;
92 using support::bformat;
93 using support::changeExtension;
94 using support::createLyXTmpDir;
95 using support::FileName;
96 using support::fileSearch;
97 using support::getEnv;
98 using support::i18nLibFileSearch;
99 using support::libFileSearch;
100 using support::package;
101 using support::prependEnvPath;
102 using support::rtrim;
103 using support::Systemcall;
104 using frontend::LyXView;
106 namespace Alert = frontend::Alert;
107 namespace os = support::os;
111 // Are we using the GUI at all? We default to true and this is changed
112 // to false when the export feature is used.
116 bool quitting; // flag, that we are quitting the program
120 // Filled with the command line arguments "foo" of "-sysdir foo" or
122 string cl_system_support;
123 string cl_user_support;
125 std::string geometryArg;
127 LyX * singleton_ = 0;
129 void showFileError(string const & error)
131 Alert::warning(_("Could not read configuration file"),
132 bformat(_("Error while reading the configuration file\n%1$s.\n"
133 "Please check your installation."), from_utf8(error)));
137 void reconfigureUserLyXDir()
139 string const configure_command = package().configure_command();
141 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
142 support::PathChanger p(package().user_support());
144 one.startscript(Systemcall::Wait, configure_command);
145 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
151 /// The main application class private implementation.
156 // Set the default User Interface language as soon as possible.
157 // The language used will be derived from the environment
159 messages_["GUI"] = Messages();
161 /// our function handler
164 BufferList buffer_list_;
166 boost::scoped_ptr<KeyMap> toplevel_keymap_;
168 boost::scoped_ptr<CmdDef> toplevel_cmddef_;
170 boost::scoped_ptr<Server> lyx_server_;
172 boost::scoped_ptr<ServerSocket> lyx_socket_;
174 boost::scoped_ptr<frontend::Application> application_;
175 /// lyx session, containing lastfiles, lastfilepos, and lastopened
176 boost::scoped_ptr<Session> session_;
178 /// Files to load at start.
179 vector<FileName> files_to_load_;
181 /// The messages translators.
182 map<string, Messages> messages_;
184 /// The file converters.
185 Converters converters_;
187 // The system converters copy after reading lyxrc.defaults.
188 Converters system_converters_;
193 Movers system_movers_;
195 /// has this user started lyx for the first time?
197 /// the parsed command line batch command if any
198 std::string batch_command;
202 frontend::Application * theApp()
205 return singleton_->pimpl_->application_.get();
219 BOOST_ASSERT(singleton_);
224 LyX const & LyX::cref()
226 BOOST_ASSERT(singleton_);
239 BufferList & LyX::bufferList()
241 return pimpl_->buffer_list_;
245 BufferList const & LyX::bufferList() const
247 return pimpl_->buffer_list_;
251 Session & LyX::session()
253 BOOST_ASSERT(pimpl_->session_.get());
254 return *pimpl_->session_.get();
258 Session const & LyX::session() const
260 BOOST_ASSERT(pimpl_->session_.get());
261 return *pimpl_->session_.get();
265 LyXFunc & LyX::lyxFunc()
267 return pimpl_->lyxfunc_;
271 LyXFunc const & LyX::lyxFunc() const
273 return pimpl_->lyxfunc_;
277 Server & LyX::server()
279 BOOST_ASSERT(pimpl_->lyx_server_.get());
280 return *pimpl_->lyx_server_.get();
284 Server const & LyX::server() const
286 BOOST_ASSERT(pimpl_->lyx_server_.get());
287 return *pimpl_->lyx_server_.get();
291 ServerSocket & LyX::socket()
293 BOOST_ASSERT(pimpl_->lyx_socket_.get());
294 return *pimpl_->lyx_socket_.get();
298 ServerSocket const & LyX::socket() const
300 BOOST_ASSERT(pimpl_->lyx_socket_.get());
301 return *pimpl_->lyx_socket_.get();
305 frontend::Application & LyX::application()
307 BOOST_ASSERT(pimpl_->application_.get());
308 return *pimpl_->application_.get();
312 frontend::Application const & LyX::application() const
314 BOOST_ASSERT(pimpl_->application_.get());
315 return *pimpl_->application_.get();
319 KeyMap & LyX::topLevelKeymap()
321 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
322 return *pimpl_->toplevel_keymap_.get();
326 CmdDef & LyX::topLevelCmdDef()
328 BOOST_ASSERT(pimpl_->toplevel_cmddef_.get());
329 return *pimpl_->toplevel_cmddef_.get();
333 Converters & LyX::converters()
335 return pimpl_->converters_;
339 Converters & LyX::systemConverters()
341 return pimpl_->system_converters_;
345 KeyMap const & LyX::topLevelKeymap() const
347 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
348 return *pimpl_->toplevel_keymap_.get();
352 Messages & LyX::getMessages(std::string const & language)
354 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
356 if (it != pimpl_->messages_.end())
359 std::pair<map<string, Messages>::iterator, bool> result =
360 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
362 BOOST_ASSERT(result.second);
363 return result.first->second;
367 Messages & LyX::getGuiMessages()
369 return pimpl_->messages_["GUI"];
373 void LyX::setGuiLanguage(std::string const & language)
375 pimpl_->messages_["GUI"] = Messages(language);
379 Buffer const * LyX::updateInset(Inset const * inset) const
381 if (quitting || !inset)
384 Buffer const * buffer_ptr = 0;
385 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
386 vector<int>::const_iterator it = view_ids.begin();
387 vector<int>::const_iterator const end = view_ids.end();
388 for (; it != end; ++it) {
390 pimpl_->application_->gui().view(*it).updateInset(inset);
398 void LyX::hideDialogs(std::string const & name, Inset * inset) const
400 if (quitting || !use_gui)
403 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
404 vector<int>::const_iterator it = view_ids.begin();
405 vector<int>::const_iterator const end = view_ids.end();
406 for (; it != end; ++it)
407 pimpl_->application_->gui().view(*it).getDialogs().
412 int LyX::exec(int & argc, char * argv[])
414 // Here we need to parse the command line. At least
415 // we need to parse for "-dbg" and "-help"
416 easyParse(argc, argv);
419 support::init_package(to_utf8(from_local8bit(argv[0])),
420 cl_system_support, cl_user_support,
421 support::top_build_dir_is_one_level_up);
422 } catch (support::ExceptionMessage const & message) {
423 if (message.type_ == support::ErrorException) {
424 Alert::error(message.title_, message.details_);
426 } else if (message.type_ == support::WarningException) {
427 Alert::warning(message.title_, message.details_);
431 // Reinit the messages machinery in case package() knows
432 // something interesting about the locale directory.
436 // FIXME: create a ConsoleApplication
437 int exit_status = init(argc, argv);
445 if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
450 BufferList::iterator begin = pimpl_->buffer_list_.begin();
452 bool final_success = false;
453 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
455 if (buf != buf->masterBuffer())
457 bool success = false;
458 buf->dispatch(pimpl_->batch_command, &success);
459 final_success |= success;
462 return !final_success;
465 // Let the frontend parse and remove all arguments that it knows
466 pimpl_->application_.reset(createApplication(argc, argv));
470 // Parse and remove all known arguments in the LyX singleton
471 // Give an error for all remaining ones.
472 int exit_status = init(argc, argv);
474 // Kill the application object before exiting.
475 pimpl_->application_.reset();
482 /* Create a CoreApplication class that will provide the main event loop
483 * and the socket callback registering. With Qt4, only QtCore
484 * library would be needed.
485 * When this is done, a server_mode could be created and the following two
486 * line would be moved out from here.
488 // Note: socket callback must be registered after init(argc, argv)
489 // such that package().temp_dir() is properly initialized.
490 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
491 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
492 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
494 // Start the real execution loop.
495 exit_status = pimpl_->application_->exec();
503 void LyX::prepareExit()
505 // Clear the clipboard and selection stack:
506 cap::clearCutStack();
507 cap::clearSelection();
509 // Set a flag that we do quitting from the program,
510 // so no refreshes are necessary.
513 // close buffers first
514 pimpl_->buffer_list_.closeAll();
516 // do any other cleanup procedures now
517 if (package().temp_dir() != package().system_temp_dir()) {
518 LYXERR(Debug::INFO, "Deleting tmp dir "
519 << package().temp_dir().absFilename());
521 if (!package().temp_dir().destroyDirectory()) {
522 docstring const msg =
523 bformat(_("Unable to remove the temporary directory %1$s"),
524 from_utf8(package().temp_dir().absFilename()));
525 Alert::warning(_("Unable to remove temporary directory"), msg);
530 if (pimpl_->session_)
531 pimpl_->session_->writeFile();
532 pimpl_->session_.reset();
533 pimpl_->lyx_server_.reset();
534 pimpl_->lyx_socket_.reset();
537 // Kill the application object before exiting. This avoids crashes
538 // when exiting on Linux.
539 if (pimpl_->application_)
540 pimpl_->application_.reset();
544 void LyX::earlyExit(int status)
546 BOOST_ASSERT(pimpl_->application_.get());
547 // LyX::pimpl_::application_ is not initialised at this
548 // point so it's safe to just exit after some cleanup.
554 int LyX::init(int & argc, char * argv[])
556 // check for any spurious extra arguments
557 // other than documents
558 for (int argi = 1; argi < argc ; ++argi) {
559 if (argv[argi][0] == '-') {
561 bformat(_("Wrong command line option `%1$s'. Exiting."),
562 from_utf8(argv[argi]))) << endl;
567 // Initialization of LyX (reads lyxrc and more)
568 LYXERR(Debug::INIT, "Initializing LyX::init...");
569 bool success = init();
570 LYXERR(Debug::INIT, "Initializing LyX::init...done");
574 for (int argi = argc - 1; argi >= 1; --argi) {
575 // get absolute path of file and add ".lyx" to
576 // the filename if necessary
577 pimpl_->files_to_load_.push_back(fileSearch(string(),
578 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
579 "lyx", support::allow_unreadable));
583 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
589 void LyX::addFileToLoad(FileName const & fname)
591 vector<FileName>::const_iterator cit = std::find(
592 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
595 if (cit == pimpl_->files_to_load_.end())
596 pimpl_->files_to_load_.push_back(fname);
600 void LyX::loadFiles()
602 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
603 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
605 for (; it != end; ++it) {
609 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
610 if (buf->loadLyXFile(*it)) {
611 ErrorList const & el = buf->errorList("Parse");
613 for_each(el.begin(), el.end(),
614 boost::bind(&LyX::printError, this, _1));
617 pimpl_->buffer_list_.release(buf);
622 void LyX::execBatchCommands()
624 // The advantage of doing this here is that the event loop
625 // is already started. So any need for interaction will be
629 // if reconfiguration is needed.
630 if (textclasslist.empty()) {
631 switch (Alert::prompt(
632 _("No textclass is found"),
633 _("LyX cannot continue because no textclass is found. "
634 "You can either reconfigure normally, or reconfigure using "
635 "default textclasses, or quit LyX."),
642 // regular reconfigure
643 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
646 // reconfigure --without-latex-config
647 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
648 " --without-latex-config"));
651 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
655 // Execute batch commands if available
656 if (pimpl_->batch_command.empty())
659 LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
661 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
665 void LyX::restoreGuiSession()
667 LyXView * view = newLyXView();
669 // if there is no valid class list, do not load any file.
670 if (textclasslist.empty())
673 // if some files were specified at command-line we assume that the
674 // user wants to edit *these* files and not to restore the session.
675 if (!pimpl_->files_to_load_.empty()) {
676 for_each(pimpl_->files_to_load_.begin(),
677 pimpl_->files_to_load_.end(),
678 bind(&LyXView::loadLyXFile, view, _1, true));
679 // clear this list to save a few bytes of RAM
680 pimpl_->files_to_load_.clear();
681 pimpl_->session_->lastOpened().clear();
683 } else if (lyxrc.load_session) {
684 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
685 // do not add to the lastfile list since these files are restored from
686 // last session, and should be already there (regular files), or should
687 // not be added at all (help files).
688 for_each(lastopened.begin(), lastopened.end(),
689 bind(&LyXView::loadLyXFile, view, _1, false));
691 // clear this list to save a few bytes of RAM
692 pimpl_->session_->lastOpened().clear();
695 BufferList::iterator I = pimpl_->buffer_list_.begin();
696 BufferList::iterator end = pimpl_->buffer_list_.end();
697 for (; I != end; ++I) {
699 if (buf != buf->masterBuffer())
704 // FIXME: Switch to the last loaded Buffer. This must not be the first one
705 // because the Buffer won't be connected in this case. The correct solution
706 // would be to avoid the manual connection of the current Buffer in LyXView.
707 if (!pimpl_->buffer_list_.empty())
708 view->setBuffer(pimpl_->buffer_list_.last());
712 LyXView * LyX::newLyXView()
717 // create the main window
718 LyXView * view = &pimpl_->application_->createView(geometryArg);
726 The SIGHUP signal does not exist on Windows and does not need to be handled.
728 Windows handles SIGFPE and SIGSEGV signals as expected.
730 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
731 cause a new thread to be spawned. This may well result in unexpected
732 behaviour by the single-threaded LyX.
734 SIGTERM signals will come only from another process actually sending
735 that signal using 'raise' in Windows' POSIX compatability layer. It will
736 not come from the general "terminate process" methods that everyone
737 actually uses (and which can't be trapped). Killing an app 'politely' on
738 Windows involves first sending a WM_CLOSE message, something that is
739 caught already by the Qt frontend.
741 For more information see:
743 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
744 ...signals are mostly useless on Windows for a variety of reasons that are
747 'UNIX Application Migration Guide, Chapter 9'
748 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
750 'How To Terminate an Application "Cleanly" in Win32'
751 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
755 static void error_handler(int err_sig)
757 // Throw away any signals other than the first one received.
758 static sig_atomic_t handling_error = false;
761 handling_error = true;
763 // We have received a signal indicating a fatal error, so
764 // try and save the data ASAP.
765 LyX::cref().emergencyCleanup();
767 // These lyxerr calls may or may not work:
769 // Signals are asynchronous, so the main program may be in a very
770 // fragile state when a signal is processed and thus while a signal
771 // handler function executes.
772 // In general, therefore, we should avoid performing any
773 // I/O operations or calling most library and system functions from
776 // This shouldn't matter here, however, as we've already invoked
781 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
785 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
788 lyxerr << "\nlyx: SIGSEGV signal caught\n"
789 "Sorry, you have found a bug in LyX. "
790 "Please read the bug-reporting instructions "
791 "in Help->Introduction and send us a bug report, "
792 "if necessary. Thanks !\nBye." << endl;
800 // Deinstall the signal handlers
802 signal(SIGHUP, SIG_DFL);
804 signal(SIGINT, SIG_DFL);
805 signal(SIGFPE, SIG_DFL);
806 signal(SIGSEGV, SIG_DFL);
807 signal(SIGTERM, SIG_DFL);
810 if (err_sig == SIGSEGV ||
811 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
813 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
822 void LyX::printError(ErrorItem const & ei)
824 docstring tmp = _("LyX: ") + ei.error + char_type(':')
826 std::cerr << to_utf8(tmp) << std::endl;
830 void LyX::initGuiFont()
832 if (lyxrc.roman_font_name.empty())
833 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
835 if (lyxrc.sans_font_name.empty())
836 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
838 if (lyxrc.typewriter_font_name.empty())
839 lyxrc.typewriter_font_name
840 = pimpl_->application_->typewriterFontName();
847 signal(SIGHUP, error_handler);
849 signal(SIGFPE, error_handler);
850 signal(SIGSEGV, error_handler);
851 signal(SIGINT, error_handler);
852 signal(SIGTERM, error_handler);
853 // SIGPIPE can be safely ignored.
855 lyxrc.tempdir_path = package().temp_dir().absFilename();
856 lyxrc.document_path = package().document_dir().absFilename();
858 if (lyxrc.template_path.empty()) {
859 lyxrc.template_path = addPath(package().system_support().absFilename(),
864 // Read configuration files
867 // This one may have been distributed along with LyX.
868 if (!readRcFile("lyxrc.dist"))
871 // Set the language defined by the distributor.
872 //setGuiLanguage(lyxrc.gui_language);
874 // Set the PATH correctly.
875 #if !defined (USE_POSIX_PACKAGING)
876 // Add the directory containing the LyX executable to the path
877 // so that LyX can find things like tex2lyx.
878 if (package().build_support().empty())
879 prependEnvPath("PATH", package().binary_dir().absFilename());
881 if (!lyxrc.path_prefix.empty())
882 prependEnvPath("PATH", lyxrc.path_prefix);
884 // Check that user LyX directory is ok.
885 if (queryUserLyXDir(package().explicit_user_support()))
886 reconfigureUserLyXDir();
888 // no need for a splash when there is no GUI
893 // This one is generated in user_support directory by lib/configure.py.
894 if (!readRcFile("lyxrc.defaults"))
897 // Query the OS to know what formats are viewed natively
898 formats.setAutoOpen();
900 // Read lyxrc.dist again to be able to override viewer auto-detection.
901 readRcFile("lyxrc.dist");
903 system_lyxrc = lyxrc;
904 system_formats = formats;
905 pimpl_->system_converters_ = pimpl_->converters_;
906 pimpl_->system_movers_ = pimpl_->movers_;
907 system_lcolor = lcolor;
909 // This one is edited through the preferences dialog.
910 if (!readRcFile("preferences"))
913 if (!readEncodingsFile("encodings", "unicodesymbols"))
915 if (!readLanguagesFile("languages"))
919 LYXERR(Debug::INIT, "Reading layouts...");
925 // read keymap and ui files in batch mode as well
926 // because InsetInfo needs to know these to produce
927 // the correct output
929 // Set the language defined by the user.
930 //setGuiLanguage(lyxrc.gui_language);
932 // Set up command definitions
933 pimpl_->toplevel_cmddef_.reset(new CmdDef);
934 pimpl_->toplevel_cmddef_->read(lyxrc.def_file);
937 pimpl_->toplevel_keymap_.reset(new KeyMap);
938 pimpl_->toplevel_keymap_->read("site");
939 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
940 // load user bind file user.bind
941 pimpl_->toplevel_keymap_->read("user");
943 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
946 if (!readUIFile(lyxrc.ui_file))
949 if (lyxerr.debugging(Debug::LYXRC))
952 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
953 if (!lyxrc.path_prefix.empty())
954 prependEnvPath("PATH", lyxrc.path_prefix);
956 FileName const document_path(lyxrc.document_path);
957 if (document_path.exists() && document_path.isDirectory())
958 package().document_dir() = document_path;
960 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
961 if (package().temp_dir().empty()) {
962 Alert::error(_("Could not create temporary directory"),
963 bformat(_("Could not create a temporary directory in\n"
964 "%1$s. Make sure that this\n"
965 "path exists and is writable and try again."),
966 from_utf8(lyxrc.tempdir_path)));
967 // createLyXTmpDir() tries sufficiently hard to create a
968 // usable temp dir, so the probability to come here is
969 // close to zero. We therefore don't try to overcome this
970 // problem with e.g. asking the user for a new path and
971 // trying again but simply exit.
975 LYXERR(Debug::INIT, "LyX tmp dir: `"
976 << package().temp_dir().absFilename() << '\'');
978 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
979 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
981 // This must happen after package initialization and after lyxrc is
982 // read, therefore it can't be done by a static object.
983 ConverterCache::init();
989 void LyX::emergencyCleanup() const
991 // what to do about tmpfiles is non-obvious. we would
992 // like to delete any we find, but our lyxdir might
993 // contain documents etc. which might be helpful on
996 pimpl_->buffer_list_.emergencyWriteAll();
998 if (pimpl_->lyx_server_)
999 pimpl_->lyx_server_->emergencyCleanup();
1000 pimpl_->lyx_server_.reset();
1001 pimpl_->lyx_socket_.reset();
1006 void LyX::deadKeyBindings(KeyMap * kbmap)
1008 // bindKeyings for transparent handling of deadkeys
1009 // The keysyms are gotten from XFree86 X11R6
1010 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1011 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1012 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1013 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1014 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1015 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1016 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1017 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1018 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1019 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1020 // nothing with this name
1021 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1022 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1023 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1024 // nothing with this name either...
1025 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1026 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1027 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1028 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1032 // return true if file does not exist or is older than configure.py.
1033 static bool needsUpdate(string const & file)
1035 // We cannot initialize configure_script directly because the package
1036 // is not initialized yet when static objects are constructed.
1037 static FileName configure_script;
1038 static bool firstrun = true;
1041 FileName(addName(package().system_support().absFilename(),
1047 FileName(addName(package().user_support().absFilename(), file));
1048 return !absfile.exists()
1049 || configure_script.lastModified() > absfile.lastModified();
1053 bool LyX::queryUserLyXDir(bool explicit_userdir)
1055 // Does user directory exist?
1056 FileName const sup = package().user_support();
1057 if (sup.exists() && sup.isDirectory()) {
1058 first_start = false;
1060 return needsUpdate("lyxrc.defaults")
1061 || needsUpdate("lyxmodules.lst")
1062 || needsUpdate("textclass.lst")
1063 || needsUpdate("packages.lst");
1066 first_start = !explicit_userdir;
1068 // If the user specified explicitly a directory, ask whether
1069 // to create it. If the user says "no", then exit.
1070 if (explicit_userdir &&
1072 _("Missing user LyX directory"),
1073 bformat(_("You have specified a non-existent user "
1074 "LyX directory, %1$s.\n"
1075 "It is needed to keep your own configuration."),
1076 from_utf8(package().user_support().absFilename())),
1078 _("&Create directory"),
1080 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1081 earlyExit(EXIT_FAILURE);
1084 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1085 from_utf8(sup.absFilename()))) << endl;
1087 if (!sup.createDirectory(0755)) {
1088 // Failed, so let's exit.
1089 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1091 earlyExit(EXIT_FAILURE);
1098 bool LyX::readRcFile(string const & name)
1100 LYXERR(Debug::INIT, "About to read " << name << "... ");
1102 FileName const lyxrc_path = libFileSearch(string(), name);
1103 if (!lyxrc_path.empty()) {
1104 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1105 if (lyxrc.read(lyxrc_path) < 0) {
1106 showFileError(name);
1110 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1116 // Read the ui file `name'
1117 bool LyX::readUIFile(string const & name, bool include)
1127 struct keyword_item uitags[ui_last - 1] = {
1128 { "include", ui_include },
1129 { "menuset", ui_menuset },
1130 { "toolbars", ui_toolbars },
1131 { "toolbarset", ui_toolbarset }
1134 // Ensure that a file is read only once (prevents include loops)
1135 static std::list<string> uifiles;
1136 std::list<string>::const_iterator it = uifiles.begin();
1137 std::list<string>::const_iterator end = uifiles.end();
1138 it = std::find(it, end, name);
1140 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1141 << "Is this an include loop?");
1145 LYXERR(Debug::INIT, "About to read " << name << "...");
1150 ui_path = libFileSearch("ui", name, "inc");
1151 if (ui_path.empty())
1152 ui_path = libFileSearch("ui",
1153 changeExtension(name, "inc"));
1156 ui_path = libFileSearch("ui", name, "ui");
1158 if (ui_path.empty()) {
1159 LYXERR(Debug::INIT, "Could not find " << name);
1160 showFileError(name);
1164 uifiles.push_back(name);
1166 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1167 Lexer lex(uitags, ui_last - 1);
1168 lex.setFile(ui_path);
1170 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1174 if (lyxerr.debugging(Debug::PARSER))
1175 lex.printTable(lyxerr);
1177 while (lex.isOK()) {
1178 switch (lex.lex()) {
1181 string const file = lex.getString();
1182 if (!readUIFile(file, true))
1187 menubackend.read(lex);
1191 toolbarbackend.readToolbars(lex);
1195 toolbarbackend.readToolbarSettings(lex);
1199 if (!rtrim(lex.getString()).empty())
1200 lex.printError("LyX::ReadUIFile: "
1201 "Unknown menu tag: `$$Token'");
1209 // Read the languages file `name'
1210 bool LyX::readLanguagesFile(string const & name)
1212 LYXERR(Debug::INIT, "About to read " << name << "...");
1214 FileName const lang_path = libFileSearch(string(), name);
1215 if (lang_path.empty()) {
1216 showFileError(name);
1219 languages.read(lang_path);
1224 // Read the encodings file `name'
1225 bool LyX::readEncodingsFile(string const & enc_name,
1226 string const & symbols_name)
1228 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1229 << symbols_name << "...");
1231 FileName const symbols_path = libFileSearch(string(), symbols_name);
1232 if (symbols_path.empty()) {
1233 showFileError(symbols_name);
1237 FileName const enc_path = libFileSearch(string(), enc_name);
1238 if (enc_path.empty()) {
1239 showFileError(enc_name);
1242 encodings.read(enc_path, symbols_path);
1251 /// return the the number of arguments consumed
1252 typedef boost::function<int(string const &, string const &)> cmd_helper;
1254 int parse_dbg(string const & arg, string const &)
1257 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1258 Debug::showTags(lyxerr);
1261 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1263 lyxerr.level(Debug::value(arg));
1264 Debug::showLevel(lyxerr, lyxerr.level());
1269 int parse_help(string const &, string const &)
1272 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1273 "Command line switches (case sensitive):\n"
1274 "\t-help summarize LyX usage\n"
1275 "\t-userdir dir set user directory to dir\n"
1276 "\t-sysdir dir set system directory to dir\n"
1277 "\t-geometry WxH+X+Y set geometry of the main window\n"
1278 "\t-dbg feature[,feature]...\n"
1279 " select the features to debug.\n"
1280 " Type `lyx -dbg' to see the list of features\n"
1281 "\t-x [--execute] command\n"
1282 " where command is a lyx command.\n"
1283 "\t-e [--export] fmt\n"
1284 " where fmt is the export format of choice.\n"
1285 " Look on Tools->Preferences->File formats->Format\n"
1286 " to get an idea which parameters should be passed.\n"
1287 "\t-i [--import] fmt file.xxx\n"
1288 " where fmt is the import format of choice\n"
1289 " and file.xxx is the file to be imported.\n"
1290 "\t-version summarize version and build info\n"
1291 "Check the LyX man page for more details.")) << endl;
1297 int parse_version(string const &, string const &)
1299 lyxerr << "LyX " << lyx_version
1300 << " (" << lyx_release_date << ")" << endl;
1301 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1303 lyxerr << lyx_version_info << endl;
1309 int parse_sysdir(string const & arg, string const &)
1312 Alert::error(_("No system directory"),
1313 _("Missing directory for -sysdir switch"));
1316 cl_system_support = arg;
1321 int parse_userdir(string const & arg, string const &)
1324 Alert::error(_("No user directory"),
1325 _("Missing directory for -userdir switch"));
1328 cl_user_support = arg;
1333 int parse_execute(string const & arg, string const &)
1336 Alert::error(_("Incomplete command"),
1337 _("Missing command string after --execute switch"));
1345 int parse_export(string const & type, string const &)
1348 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1349 "--export switch")) << endl;
1352 batch = "buffer-export " + type;
1358 int parse_import(string const & type, string const & file)
1361 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1362 "--import switch")) << endl;
1366 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1370 batch = "buffer-import " + type + ' ' + file;
1375 int parse_geometry(string const & arg1, string const &)
1378 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1379 // remove also the arg
1382 // don't remove "-geometry"
1391 void LyX::easyParse(int & argc, char * argv[])
1393 std::map<string, cmd_helper> cmdmap;
1395 cmdmap["-dbg"] = parse_dbg;
1396 cmdmap["-help"] = parse_help;
1397 cmdmap["--help"] = parse_help;
1398 cmdmap["-version"] = parse_version;
1399 cmdmap["--version"] = parse_version;
1400 cmdmap["-sysdir"] = parse_sysdir;
1401 cmdmap["-userdir"] = parse_userdir;
1402 cmdmap["-x"] = parse_execute;
1403 cmdmap["--execute"] = parse_execute;
1404 cmdmap["-e"] = parse_export;
1405 cmdmap["--export"] = parse_export;
1406 cmdmap["-i"] = parse_import;
1407 cmdmap["--import"] = parse_import;
1408 cmdmap["-geometry"] = parse_geometry;
1410 for (int i = 1; i < argc; ++i) {
1411 std::map<string, cmd_helper>::const_iterator it
1412 = cmdmap.find(argv[i]);
1414 // don't complain if not found - may be parsed later
1415 if (it == cmdmap.end())
1419 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1421 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1423 int const remove = 1 + it->second(arg, arg2);
1425 // Now, remove used arguments by shifting
1426 // the following ones remove places down.
1429 for (int j = i; j < argc; ++j)
1430 argv[j] = argv[j + remove];
1435 pimpl_->batch_command = batch;
1439 FuncStatus getStatus(FuncRequest const & action)
1441 return LyX::ref().lyxFunc().getStatus(action);
1445 void dispatch(FuncRequest const & action)
1447 LyX::ref().lyxFunc().dispatch(action);
1451 BufferList & theBufferList()
1453 return LyX::ref().bufferList();
1457 LyXFunc & theLyXFunc()
1459 return LyX::ref().lyxFunc();
1463 Server & theServer()
1465 // FIXME: this should not be use_gui dependent
1466 BOOST_ASSERT(use_gui);
1467 return LyX::ref().server();
1471 ServerSocket & theServerSocket()
1473 // FIXME: this should not be use_gui dependent
1474 BOOST_ASSERT(use_gui);
1475 return LyX::ref().socket();
1479 KeyMap & theTopLevelKeymap()
1481 return LyX::ref().topLevelKeymap();
1485 Converters & theConverters()
1487 return LyX::ref().converters();
1491 Converters & theSystemConverters()
1493 return LyX::ref().systemConverters();
1497 Movers & theMovers()
1499 return LyX::ref().pimpl_->movers_;
1503 Mover const & getMover(std::string const & fmt)
1505 return LyX::ref().pimpl_->movers_(fmt);
1509 void setMover(std::string const & fmt, std::string const & command)
1511 LyX::ref().pimpl_->movers_.set(fmt, command);
1515 Movers & theSystemMovers()
1517 return LyX::ref().pimpl_->system_movers_;
1521 Messages & getMessages(std::string const & language)
1523 return LyX::ref().getMessages(language);
1527 Messages & getGuiMessages()
1529 return LyX::ref().getGuiMessages();