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/lyxlib.h"
58 #include "support/convert.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.
152 struct LyX::Singletons
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_;
194 Movers system_movers_;
198 frontend::Application * theApp()
201 return singleton_->pimpl_->application_.get();
215 BOOST_ASSERT(singleton_);
220 LyX const & LyX::cref()
222 BOOST_ASSERT(singleton_);
231 pimpl_ = new Singletons;
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 Buffer const * LyX::updateInset(Inset const * inset) const
377 if (quitting || !inset)
380 Buffer const * buffer_ptr = 0;
381 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
382 vector<int>::const_iterator it = view_ids.begin();
383 vector<int>::const_iterator const end = view_ids.end();
384 for (; it != end; ++it) {
386 pimpl_->application_->gui().view(*it).updateInset(inset);
394 void LyX::hideDialogs(std::string const & name, Inset * inset) const
396 if (quitting || !use_gui)
399 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
400 vector<int>::const_iterator it = view_ids.begin();
401 vector<int>::const_iterator const end = view_ids.end();
402 for (; it != end; ++it)
403 pimpl_->application_->gui().view(*it).getDialogs().
408 int LyX::exec(int & argc, char * argv[])
410 // Here we need to parse the command line. At least
411 // we need to parse for "-dbg" and "-help"
412 easyParse(argc, argv);
415 support::init_package(to_utf8(from_local8bit(argv[0])),
416 cl_system_support, cl_user_support,
417 support::top_build_dir_is_one_level_up);
418 } catch (support::ExceptionMessage const & message) {
419 if (message.type_ == support::ErrorException) {
420 Alert::error(message.title_, message.details_);
422 } else if (message.type_ == support::WarningException) {
423 Alert::warning(message.title_, message.details_);
427 // Reinit the messages machinery in case package() knows
428 // something interesting about the locale directory.
432 // FIXME: create a ConsoleApplication
433 int exit_status = init(argc, argv);
441 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
446 BufferList::iterator begin = pimpl_->buffer_list_.begin();
448 bool final_success = false;
449 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
451 if (buf != buf->masterBuffer())
453 bool success = false;
454 buf->dispatch(batch_command, &success);
455 final_success |= success;
458 return !final_success;
461 // Let the frontend parse and remove all arguments that it knows
462 pimpl_->application_.reset(createApplication(argc, argv));
466 // Parse and remove all known arguments in the LyX singleton
467 // Give an error for all remaining ones.
468 int exit_status = init(argc, argv);
470 // Kill the application object before exiting.
471 pimpl_->application_.reset();
478 /* Create a CoreApplication class that will provide the main event loop
479 * and the socket callback registering. With Qt4, only QtCore
480 * library would be needed.
481 * When this is done, a server_mode could be created and the following two
482 * line would be moved out from here.
484 // Note: socket callback must be registered after init(argc, argv)
485 // such that package().temp_dir() is properly initialized.
486 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
487 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
488 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
490 // Start the real execution loop.
491 exit_status = pimpl_->application_->exec();
499 void LyX::prepareExit()
501 // Clear the clipboard and selection stack:
502 cap::clearCutStack();
503 cap::clearSelection();
505 // Set a flag that we do quitting from the program,
506 // so no refreshes are necessary.
509 // close buffers first
510 pimpl_->buffer_list_.closeAll();
512 // do any other cleanup procedures now
513 if (package().temp_dir() != package().system_temp_dir()) {
514 LYXERR(Debug::INFO) << "Deleting tmp dir "
515 << package().temp_dir().absFilename() << endl;
517 if (!package().temp_dir().destroyDirectory()) {
518 docstring const msg =
519 bformat(_("Unable to remove the temporary directory %1$s"),
520 from_utf8(package().temp_dir().absFilename()));
521 Alert::warning(_("Unable to remove temporary directory"), msg);
526 if (pimpl_->session_)
527 pimpl_->session_->writeFile();
528 pimpl_->session_.reset();
529 pimpl_->lyx_server_.reset();
530 pimpl_->lyx_socket_.reset();
533 // Kill the application object before exiting. This avoids crashes
534 // when exiting on Linux.
535 if (pimpl_->application_)
536 pimpl_->application_.reset();
540 void LyX::earlyExit(int status)
542 BOOST_ASSERT(pimpl_->application_.get());
543 // LyX::pimpl_::application_ is not initialised at this
544 // point so it's safe to just exit after some cleanup.
550 int LyX::init(int & argc, char * argv[])
552 // check for any spurious extra arguments
553 // other than documents
554 for (int argi = 1; argi < argc ; ++argi) {
555 if (argv[argi][0] == '-') {
557 bformat(_("Wrong command line option `%1$s'. Exiting."),
558 from_utf8(argv[argi]))) << endl;
563 // Initialization of LyX (reads lyxrc and more)
564 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
565 bool success = init();
566 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
570 for (int argi = argc - 1; argi >= 1; --argi) {
571 // get absolute path of file and add ".lyx" to
572 // the filename if necessary
573 pimpl_->files_to_load_.push_back(fileSearch(string(),
574 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
575 "lyx", support::allow_unreadable));
579 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
585 void LyX::addFileToLoad(FileName const & fname)
587 vector<FileName>::const_iterator cit = std::find(
588 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
591 if (cit == pimpl_->files_to_load_.end())
592 pimpl_->files_to_load_.push_back(fname);
596 void LyX::loadFiles()
598 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
599 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
601 for (; it != end; ++it) {
605 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
606 if (buf->loadLyXFile(*it)) {
607 ErrorList const & el = buf->errorList("Parse");
609 for_each(el.begin(), el.end(),
610 boost::bind(&LyX::printError, this, _1));
613 pimpl_->buffer_list_.release(buf);
618 void LyX::execBatchCommands()
620 // The advantage of doing this here is that the event loop
621 // is already started. So any need for interaction will be
625 // if reconfiguration is needed.
626 if (textclasslist.empty()) {
627 switch (Alert::prompt(
628 _("No textclass is found"),
629 _("LyX cannot continue because no textclass is found. "
630 "You can either reconfigure normally, or reconfigure using "
631 "default textclasses, or quit LyX."),
638 // regular reconfigure
639 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
642 // reconfigure --without-latex-config
643 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
644 " --without-latex-config"));
647 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
651 // Execute batch commands if available
652 if (batch_command.empty())
655 LYXERR(Debug::INIT) << "About to handle -x '"
656 << batch_command << '\'' << endl;
658 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
662 void LyX::restoreGuiSession()
664 LyXView * view = newLyXView();
666 // if there is no valid class list, do not load any file.
667 if (textclasslist.empty())
670 // if some files were specified at command-line we assume that the
671 // user wants to edit *these* files and not to restore the session.
672 if (!pimpl_->files_to_load_.empty()) {
673 for_each(pimpl_->files_to_load_.begin(),
674 pimpl_->files_to_load_.end(),
675 bind(&LyXView::loadLyXFile, view, _1, true));
676 // clear this list to save a few bytes of RAM
677 pimpl_->files_to_load_.clear();
678 pimpl_->session_->lastOpened().clear();
680 } else if (lyxrc.load_session) {
681 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
682 // do not add to the lastfile list since these files are restored from
683 // last session, and should be already there (regular files), or should
684 // not be added at all (help files).
685 for_each(lastopened.begin(), lastopened.end(),
686 bind(&LyXView::loadLyXFile, view, _1, false));
688 // clear this list to save a few bytes of RAM
689 pimpl_->session_->lastOpened().clear();
692 BufferList::iterator I = pimpl_->buffer_list_.begin();
693 BufferList::iterator end = pimpl_->buffer_list_.end();
694 for (; I != end; ++I) {
696 if (buf != buf->masterBuffer())
701 // FIXME: Switch to the last loaded Buffer. This must not be the first one
702 // because the Buffer won't be connected in this case. The correct solution
703 // would be to avoid the manual connection of the current Buffer in LyXView.
704 if (!pimpl_->buffer_list_.empty())
705 view->setBuffer(pimpl_->buffer_list_.last());
709 LyXView * LyX::newLyXView()
714 // determine windows size and position, from lyxrc and/or session
716 unsigned int width = 690;
717 unsigned int height = 510;
718 // default icon size, will be overwritten by stored session value
719 unsigned int iconSizeXY = 0;
720 int maximized = LyXView::NotMaximized;
722 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
723 width = lyxrc.geometry_width;
724 height = lyxrc.geometry_height;
726 // if lyxrc returns (0,0), then use session info
728 string val = session().sessionInfo().load("WindowWidth");
730 width = convert<unsigned int>(val);
731 val = session().sessionInfo().load("WindowHeight");
733 height = convert<unsigned int>(val);
734 val = session().sessionInfo().load("WindowMaximized");
736 maximized = convert<int>(val);
737 val = session().sessionInfo().load("IconSizeXY");
739 iconSizeXY = convert<unsigned int>(val);
742 // if user wants to restore window position
745 if (lyxrc.geometry_xysaved) {
746 string val = session().sessionInfo().load("WindowPosX");
748 posx = convert<int>(val);
749 val = session().sessionInfo().load("WindowPosY");
751 posy = convert<int>(val);
754 if (!geometryArg.empty())
760 // create the main window
761 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
769 The SIGHUP signal does not exist on Windows and does not need to be handled.
771 Windows handles SIGFPE and SIGSEGV signals as expected.
773 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
774 cause a new thread to be spawned. This may well result in unexpected
775 behaviour by the single-threaded LyX.
777 SIGTERM signals will come only from another process actually sending
778 that signal using 'raise' in Windows' POSIX compatability layer. It will
779 not come from the general "terminate process" methods that everyone
780 actually uses (and which can't be trapped). Killing an app 'politely' on
781 Windows involves first sending a WM_CLOSE message, something that is
782 caught already by the Qt frontend.
784 For more information see:
786 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
787 ...signals are mostly useless on Windows for a variety of reasons that are
790 'UNIX Application Migration Guide, Chapter 9'
791 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
793 'How To Terminate an Application "Cleanly" in Win32'
794 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
798 static void error_handler(int err_sig)
800 // Throw away any signals other than the first one received.
801 static sig_atomic_t handling_error = false;
804 handling_error = true;
806 // We have received a signal indicating a fatal error, so
807 // try and save the data ASAP.
808 LyX::cref().emergencyCleanup();
810 // These lyxerr calls may or may not work:
812 // Signals are asynchronous, so the main program may be in a very
813 // fragile state when a signal is processed and thus while a signal
814 // handler function executes.
815 // In general, therefore, we should avoid performing any
816 // I/O operations or calling most library and system functions from
819 // This shouldn't matter here, however, as we've already invoked
824 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
828 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
831 lyxerr << "\nlyx: SIGSEGV signal caught\n"
832 "Sorry, you have found a bug in LyX. "
833 "Please read the bug-reporting instructions "
834 "in Help->Introduction and send us a bug report, "
835 "if necessary. Thanks !\nBye." << endl;
843 // Deinstall the signal handlers
845 signal(SIGHUP, SIG_DFL);
847 signal(SIGINT, SIG_DFL);
848 signal(SIGFPE, SIG_DFL);
849 signal(SIGSEGV, SIG_DFL);
850 signal(SIGTERM, SIG_DFL);
853 if (err_sig == SIGSEGV ||
854 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
856 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
865 void LyX::printError(ErrorItem const & ei)
867 docstring tmp = _("LyX: ") + ei.error + char_type(':')
869 std::cerr << to_utf8(tmp) << std::endl;
873 void LyX::initGuiFont()
875 if (lyxrc.roman_font_name.empty())
876 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
878 if (lyxrc.sans_font_name.empty())
879 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
881 if (lyxrc.typewriter_font_name.empty())
882 lyxrc.typewriter_font_name
883 = pimpl_->application_->typewriterFontName();
890 signal(SIGHUP, error_handler);
892 signal(SIGFPE, error_handler);
893 signal(SIGSEGV, error_handler);
894 signal(SIGINT, error_handler);
895 signal(SIGTERM, error_handler);
896 // SIGPIPE can be safely ignored.
898 lyxrc.tempdir_path = package().temp_dir().absFilename();
899 lyxrc.document_path = package().document_dir().absFilename();
901 if (lyxrc.template_path.empty()) {
902 lyxrc.template_path = addPath(package().system_support().absFilename(),
907 // Read configuration files
910 // This one may have been distributed along with LyX.
911 if (!readRcFile("lyxrc.dist"))
914 // Set the language defined by the distributor.
915 //setGuiLanguage(lyxrc.gui_language);
917 // Set the PATH correctly.
918 #if !defined (USE_POSIX_PACKAGING)
919 // Add the directory containing the LyX executable to the path
920 // so that LyX can find things like tex2lyx.
921 if (package().build_support().empty())
922 prependEnvPath("PATH", package().binary_dir().absFilename());
924 if (!lyxrc.path_prefix.empty())
925 prependEnvPath("PATH", lyxrc.path_prefix);
927 // Check that user LyX directory is ok.
928 if (queryUserLyXDir(package().explicit_user_support()))
929 reconfigureUserLyXDir();
931 // no need for a splash when there is no GUI
936 // This one is generated in user_support directory by lib/configure.py.
937 if (!readRcFile("lyxrc.defaults"))
940 // Query the OS to know what formats are viewed natively
941 formats.setAutoOpen();
943 // Read lyxrc.dist again to be able to override viewer auto-detection.
944 readRcFile("lyxrc.dist");
946 system_lyxrc = lyxrc;
947 system_formats = formats;
948 pimpl_->system_converters_ = pimpl_->converters_;
949 pimpl_->system_movers_ = pimpl_->movers_;
950 system_lcolor = lcolor;
952 // This one is edited through the preferences dialog.
953 if (!readRcFile("preferences"))
956 if (!readEncodingsFile("encodings", "unicodesymbols"))
958 if (!readLanguagesFile("languages"))
962 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
968 // read keymap and ui files in batch mode as well
969 // because InsetInfo needs to know these to produce
970 // the correct output
972 // Set the language defined by the user.
973 //setGuiLanguage(lyxrc.gui_language);
975 // Set up command definitions
976 pimpl_->toplevel_cmddef_.reset(new CmdDef);
977 pimpl_->toplevel_cmddef_->read(lyxrc.def_file);
980 pimpl_->toplevel_keymap_.reset(new KeyMap);
981 pimpl_->toplevel_keymap_->read("site");
982 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
983 // load user bind file user.bind
984 pimpl_->toplevel_keymap_->read("user");
986 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
989 if (!readUIFile(lyxrc.ui_file))
992 if (lyxerr.debugging(Debug::LYXRC))
995 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
996 if (!lyxrc.path_prefix.empty())
997 prependEnvPath("PATH", lyxrc.path_prefix);
999 FileName const document_path(lyxrc.document_path);
1000 if (document_path.exists() && document_path.isDirectory())
1001 package().document_dir() = document_path;
1003 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
1004 if (package().temp_dir().empty()) {
1005 Alert::error(_("Could not create temporary directory"),
1006 bformat(_("Could not create a temporary directory in\n"
1007 "%1$s. Make sure that this\n"
1008 "path exists and is writable and try again."),
1009 from_utf8(lyxrc.tempdir_path)));
1010 // createLyXTmpDir() tries sufficiently hard to create a
1011 // usable temp dir, so the probability to come here is
1012 // close to zero. We therefore don't try to overcome this
1013 // problem with e.g. asking the user for a new path and
1014 // trying again but simply exit.
1018 LYXERR(Debug::INIT) << "LyX tmp dir: `"
1019 << package().temp_dir().absFilename()
1022 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
1023 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1025 // This must happen after package initialization and after lyxrc is
1026 // read, therefore it can't be done by a static object.
1027 ConverterCache::init();
1033 void LyX::emergencyCleanup() const
1035 // what to do about tmpfiles is non-obvious. we would
1036 // like to delete any we find, but our lyxdir might
1037 // contain documents etc. which might be helpful on
1040 pimpl_->buffer_list_.emergencyWriteAll();
1042 if (pimpl_->lyx_server_)
1043 pimpl_->lyx_server_->emergencyCleanup();
1044 pimpl_->lyx_server_.reset();
1045 pimpl_->lyx_socket_.reset();
1050 void LyX::deadKeyBindings(KeyMap * kbmap)
1052 // bindKeyings for transparent handling of deadkeys
1053 // The keysyms are gotten from XFree86 X11R6
1054 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1055 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1056 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1057 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1058 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1059 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1060 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1061 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1062 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1063 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1064 // nothing with this name
1065 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1066 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1067 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1068 // nothing with this name either...
1069 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1070 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1071 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1072 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1076 // return true if file does not exist or is older than configure.py.
1077 static bool needsUpdate(string const & file)
1079 // We cannot initialize configure_script directly because the package
1080 // is not initialized yet when static objects are constructed.
1081 static FileName configure_script;
1082 static bool firstrun = true;
1085 FileName(addName(package().system_support().absFilename(),
1091 FileName(addName(package().user_support().absFilename(), file));
1092 return !absfile.exists()
1093 || configure_script.lastModified() > absfile.lastModified();
1097 bool LyX::queryUserLyXDir(bool explicit_userdir)
1099 // Does user directory exist?
1100 FileName const sup = package().user_support();
1101 if (sup.exists() && sup.isDirectory()) {
1102 first_start = false;
1104 return needsUpdate("lyxrc.defaults")
1105 || needsUpdate("lyxmodules.lst")
1106 || needsUpdate("textclass.lst")
1107 || needsUpdate("packages.lst");
1110 first_start = !explicit_userdir;
1112 // If the user specified explicitly a directory, ask whether
1113 // to create it. If the user says "no", then exit.
1114 if (explicit_userdir &&
1116 _("Missing user LyX directory"),
1117 bformat(_("You have specified a non-existent user "
1118 "LyX directory, %1$s.\n"
1119 "It is needed to keep your own configuration."),
1120 from_utf8(package().user_support().absFilename())),
1122 _("&Create directory"),
1124 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1125 earlyExit(EXIT_FAILURE);
1128 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1129 from_utf8(sup.absFilename()))) << endl;
1131 if (!sup.createDirectory(0755)) {
1132 // Failed, so let's exit.
1133 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1135 earlyExit(EXIT_FAILURE);
1142 bool LyX::readRcFile(string const & name)
1144 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1146 FileName const lyxrc_path = libFileSearch(string(), name);
1147 if (!lyxrc_path.empty()) {
1149 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1151 if (lyxrc.read(lyxrc_path) < 0) {
1152 showFileError(name);
1156 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1162 // Read the ui file `name'
1163 bool LyX::readUIFile(string const & name, bool include)
1173 struct keyword_item uitags[ui_last - 1] = {
1174 { "include", ui_include },
1175 { "menuset", ui_menuset },
1176 { "toolbars", ui_toolbars },
1177 { "toolbarset", ui_toolbarset }
1180 // Ensure that a file is read only once (prevents include loops)
1181 static std::list<string> uifiles;
1182 std::list<string>::const_iterator it = uifiles.begin();
1183 std::list<string>::const_iterator end = uifiles.end();
1184 it = std::find(it, end, name);
1186 LYXERR(Debug::INIT) << "UI file '" << name
1187 << "' has been read already. "
1188 << "Is this an include loop?"
1193 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1198 ui_path = libFileSearch("ui", name, "inc");
1199 if (ui_path.empty())
1200 ui_path = libFileSearch("ui",
1201 changeExtension(name, "inc"));
1204 ui_path = libFileSearch("ui", name, "ui");
1206 if (ui_path.empty()) {
1207 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1208 showFileError(name);
1212 uifiles.push_back(name);
1214 LYXERR(Debug::INIT) << "Found " << name
1215 << " in " << ui_path << endl;
1216 Lexer lex(uitags, ui_last - 1);
1217 lex.setFile(ui_path);
1219 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1223 if (lyxerr.debugging(Debug::PARSER))
1224 lex.printTable(lyxerr);
1226 while (lex.isOK()) {
1227 switch (lex.lex()) {
1230 string const file = lex.getString();
1231 if (!readUIFile(file, true))
1236 menubackend.read(lex);
1240 toolbarbackend.readToolbars(lex);
1244 toolbarbackend.readToolbarSettings(lex);
1248 if (!rtrim(lex.getString()).empty())
1249 lex.printError("LyX::ReadUIFile: "
1250 "Unknown menu tag: `$$Token'");
1258 // Read the languages file `name'
1259 bool LyX::readLanguagesFile(string const & name)
1261 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1263 FileName const lang_path = libFileSearch(string(), name);
1264 if (lang_path.empty()) {
1265 showFileError(name);
1268 languages.read(lang_path);
1273 // Read the encodings file `name'
1274 bool LyX::readEncodingsFile(string const & enc_name,
1275 string const & symbols_name)
1277 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1278 << symbols_name << "..." << endl;
1280 FileName const symbols_path = libFileSearch(string(), symbols_name);
1281 if (symbols_path.empty()) {
1282 showFileError(symbols_name);
1286 FileName const enc_path = libFileSearch(string(), enc_name);
1287 if (enc_path.empty()) {
1288 showFileError(enc_name);
1291 encodings.read(enc_path, symbols_path);
1300 /// return the the number of arguments consumed
1301 typedef boost::function<int(string const &, string const &)> cmd_helper;
1303 int parse_dbg(string const & arg, string const &)
1306 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1307 Debug::showTags(lyxerr);
1310 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1312 lyxerr.level(Debug::value(arg));
1313 Debug::showLevel(lyxerr, lyxerr.level());
1318 int parse_help(string const &, string const &)
1321 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1322 "Command line switches (case sensitive):\n"
1323 "\t-help summarize LyX usage\n"
1324 "\t-userdir dir set user directory to dir\n"
1325 "\t-sysdir dir set system directory to dir\n"
1326 "\t-geometry WxH+X+Y set geometry of the main window\n"
1327 "\t-dbg feature[,feature]...\n"
1328 " select the features to debug.\n"
1329 " Type `lyx -dbg' to see the list of features\n"
1330 "\t-x [--execute] command\n"
1331 " where command is a lyx command.\n"
1332 "\t-e [--export] fmt\n"
1333 " where fmt is the export format of choice.\n"
1334 "\t-i [--import] fmt file.xxx\n"
1335 " where fmt is the import format of choice\n"
1336 " and file.xxx is the file to be imported.\n"
1337 "\t-version summarize version and build info\n"
1338 "Check the LyX man page for more details.")) << endl;
1344 int parse_version(string const &, string const &)
1346 lyxerr << "LyX " << lyx_version
1347 << " (" << lyx_release_date << ")" << endl;
1348 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1350 lyxerr << lyx_version_info << endl;
1356 int parse_sysdir(string const & arg, string const &)
1359 Alert::error(_("No system directory"),
1360 _("Missing directory for -sysdir switch"));
1363 cl_system_support = arg;
1368 int parse_userdir(string const & arg, string const &)
1371 Alert::error(_("No user directory"),
1372 _("Missing directory for -userdir switch"));
1375 cl_user_support = arg;
1380 int parse_execute(string const & arg, string const &)
1383 Alert::error(_("Incomplete command"),
1384 _("Missing command string after --execute switch"));
1392 int parse_export(string const & type, string const &)
1395 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1396 "--export switch")) << endl;
1399 batch = "buffer-export " + type;
1405 int parse_import(string const & type, string const & file)
1408 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1409 "--import switch")) << endl;
1413 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1417 batch = "buffer-import " + type + ' ' + file;
1422 int parse_geometry(string const & arg1, string const &)
1425 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1426 // remove also the arg
1429 // don't remove "-geometry"
1438 void LyX::easyParse(int & argc, char * argv[])
1440 std::map<string, cmd_helper> cmdmap;
1442 cmdmap["-dbg"] = parse_dbg;
1443 cmdmap["-help"] = parse_help;
1444 cmdmap["--help"] = parse_help;
1445 cmdmap["-version"] = parse_version;
1446 cmdmap["--version"] = parse_version;
1447 cmdmap["-sysdir"] = parse_sysdir;
1448 cmdmap["-userdir"] = parse_userdir;
1449 cmdmap["-x"] = parse_execute;
1450 cmdmap["--execute"] = parse_execute;
1451 cmdmap["-e"] = parse_export;
1452 cmdmap["--export"] = parse_export;
1453 cmdmap["-i"] = parse_import;
1454 cmdmap["--import"] = parse_import;
1455 cmdmap["-geometry"] = parse_geometry;
1457 for (int i = 1; i < argc; ++i) {
1458 std::map<string, cmd_helper>::const_iterator it
1459 = cmdmap.find(argv[i]);
1461 // don't complain if not found - may be parsed later
1462 if (it == cmdmap.end())
1466 (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1468 (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1470 int const remove = 1 + it->second(arg, arg2);
1472 // Now, remove used arguments by shifting
1473 // the following ones remove places down.
1476 for (int j = i; j < argc; ++j)
1477 argv[j] = argv[j + remove];
1482 batch_command = batch;
1486 FuncStatus getStatus(FuncRequest const & action)
1488 return LyX::ref().lyxFunc().getStatus(action);
1492 void dispatch(FuncRequest const & action)
1494 LyX::ref().lyxFunc().dispatch(action);
1498 BufferList & theBufferList()
1500 return LyX::ref().bufferList();
1504 LyXFunc & theLyXFunc()
1506 return LyX::ref().lyxFunc();
1510 Server & theServer()
1512 // FIXME: this should not be use_gui dependent
1513 BOOST_ASSERT(use_gui);
1514 return LyX::ref().server();
1518 ServerSocket & theServerSocket()
1520 // FIXME: this should not be use_gui dependent
1521 BOOST_ASSERT(use_gui);
1522 return LyX::ref().socket();
1526 KeyMap & theTopLevelKeymap()
1528 return LyX::ref().topLevelKeymap();
1532 Converters & theConverters()
1534 return LyX::ref().converters();
1538 Converters & theSystemConverters()
1540 return LyX::ref().systemConverters();
1544 Movers & theMovers()
1546 return LyX::ref().pimpl_->movers_;
1550 Mover const & getMover(std::string const & fmt)
1552 return LyX::ref().pimpl_->movers_(fmt);
1556 void setMover(std::string const & fmt, std::string const & command)
1558 LyX::ref().pimpl_->movers_.set(fmt, command);
1562 Movers & theSystemMovers()
1564 return LyX::ref().pimpl_->system_movers_;
1568 Messages & getMessages(std::string const & language)
1570 return LyX::ref().getMessages(language);
1574 Messages & getGuiMessages()
1576 return LyX::ref().getGuiMessages();