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>
81 #ifndef CXX_GLOBAL_CSTD
89 using support::addName;
90 using support::addPath;
91 using support::bformat;
92 using support::changeExtension;
93 using support::createLyXTmpDir;
94 using support::FileName;
95 using support::fileSearch;
96 using support::getEnv;
97 using support::i18nLibFileSearch;
98 using support::libFileSearch;
99 using support::package;
100 using support::prependEnvPath;
101 using support::rtrim;
102 using support::Systemcall;
103 using frontend::LyXView;
105 namespace Alert = frontend::Alert;
106 namespace os = support::os;
110 /// are we using the GUI at all?
112 * We default to true and this is changed 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::Path 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();
214 BOOST_ASSERT(singleton_);
219 LyX const & LyX::cref()
221 BOOST_ASSERT(singleton_);
230 pimpl_.reset(new Singletons);
234 BufferList & LyX::bufferList()
236 return pimpl_->buffer_list_;
240 BufferList const & LyX::bufferList() const
242 return pimpl_->buffer_list_;
246 Session & LyX::session()
248 BOOST_ASSERT(pimpl_->session_.get());
249 return *pimpl_->session_.get();
253 Session const & LyX::session() const
255 BOOST_ASSERT(pimpl_->session_.get());
256 return *pimpl_->session_.get();
260 LyXFunc & LyX::lyxFunc()
262 return pimpl_->lyxfunc_;
266 LyXFunc const & LyX::lyxFunc() const
268 return pimpl_->lyxfunc_;
272 Server & LyX::server()
274 BOOST_ASSERT(pimpl_->lyx_server_.get());
275 return *pimpl_->lyx_server_.get();
279 Server const & LyX::server() const
281 BOOST_ASSERT(pimpl_->lyx_server_.get());
282 return *pimpl_->lyx_server_.get();
286 ServerSocket & LyX::socket()
288 BOOST_ASSERT(pimpl_->lyx_socket_.get());
289 return *pimpl_->lyx_socket_.get();
293 ServerSocket const & LyX::socket() const
295 BOOST_ASSERT(pimpl_->lyx_socket_.get());
296 return *pimpl_->lyx_socket_.get();
300 frontend::Application & LyX::application()
302 BOOST_ASSERT(pimpl_->application_.get());
303 return *pimpl_->application_.get();
307 frontend::Application const & LyX::application() const
309 BOOST_ASSERT(pimpl_->application_.get());
310 return *pimpl_->application_.get();
314 KeyMap & LyX::topLevelKeymap()
316 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
317 return *pimpl_->toplevel_keymap_.get();
321 CmdDef & LyX::topLevelCmdDef()
323 BOOST_ASSERT(pimpl_->toplevel_cmddef_.get());
324 return *pimpl_->toplevel_cmddef_.get();
328 Converters & LyX::converters()
330 return pimpl_->converters_;
334 Converters & LyX::systemConverters()
336 return pimpl_->system_converters_;
340 KeyMap const & LyX::topLevelKeymap() const
342 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
343 return *pimpl_->toplevel_keymap_.get();
347 Messages & LyX::getMessages(std::string const & language)
349 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
351 if (it != pimpl_->messages_.end())
354 std::pair<map<string, Messages>::iterator, bool> result =
355 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
357 BOOST_ASSERT(result.second);
358 return result.first->second;
362 Messages & LyX::getGuiMessages()
364 return pimpl_->messages_["GUI"];
368 void LyX::setGuiLanguage(std::string const & language)
370 pimpl_->messages_["GUI"] = Messages(language);
374 Buffer const * LyX::updateInset(Inset const * inset) const
376 if (quitting || !inset)
379 Buffer const * buffer_ptr = 0;
380 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
381 vector<int>::const_iterator it = view_ids.begin();
382 vector<int>::const_iterator const end = view_ids.end();
383 for (; it != end; ++it) {
385 pimpl_->application_->gui().view(*it).updateInset(inset);
393 void LyX::hideDialogs(std::string const & name, Inset * inset) const
395 if (quitting || !use_gui)
398 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
399 vector<int>::const_iterator it = view_ids.begin();
400 vector<int>::const_iterator const end = view_ids.end();
401 for (; it != end; ++it)
402 pimpl_->application_->gui().view(*it).getDialogs().
407 int LyX::exec(int & argc, char * argv[])
409 // Here we need to parse the command line. At least
410 // we need to parse for "-dbg" and "-help"
411 easyParse(argc, argv);
414 support::init_package(to_utf8(from_local8bit(argv[0])),
415 cl_system_support, cl_user_support,
416 support::top_build_dir_is_one_level_up);
417 } catch (support::ExceptionMessage const & message) {
418 if (message.type_ == support::ErrorException) {
419 Alert::error(message.title_, message.details_);
421 } else if (message.type_ == support::WarningException) {
422 Alert::warning(message.title_, message.details_);
426 // Reinit the messages machinery in case package() knows
427 // something interesting about the locale directory.
431 // FIXME: create a ConsoleApplication
432 int exit_status = init(argc, argv);
440 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
445 BufferList::iterator begin = pimpl_->buffer_list_.begin();
447 bool final_success = false;
448 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
450 if (buf != buf->masterBuffer())
452 bool success = false;
453 buf->dispatch(batch_command, &success);
454 final_success |= success;
457 return !final_success;
460 // Let the frontend parse and remove all arguments that it knows
461 pimpl_->application_.reset(createApplication(argc, argv));
465 // Parse and remove all known arguments in the LyX singleton
466 // Give an error for all remaining ones.
467 int exit_status = init(argc, argv);
469 // Kill the application object before exiting.
470 pimpl_->application_.reset();
477 /* Create a CoreApplication class that will provide the main event loop
478 * and the socket callback registering. With Qt4, only QtCore
479 * library would be needed.
480 * When this is done, a server_mode could be created and the following two
481 * line would be moved out from here.
483 // Note: socket callback must be registered after init(argc, argv)
484 // such that package().temp_dir() is properly initialized.
485 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
486 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
487 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
489 // Start the real execution loop.
490 exit_status = pimpl_->application_->exec();
498 void LyX::prepareExit()
500 // Clear the clipboard and selection stack:
501 cap::clearCutStack();
502 cap::clearSelection();
504 // Set a flag that we do quitting from the program,
505 // so no refreshes are necessary.
508 // close buffers first
509 pimpl_->buffer_list_.closeAll();
511 // do any other cleanup procedures now
512 if (package().temp_dir() != package().system_temp_dir()) {
513 LYXERR(Debug::INFO) << "Deleting tmp dir "
514 << package().temp_dir().absFilename() << endl;
516 if (!package().temp_dir().destroyDirectory()) {
517 docstring const msg =
518 bformat(_("Unable to remove the temporary directory %1$s"),
519 from_utf8(package().temp_dir().absFilename()));
520 Alert::warning(_("Unable to remove temporary directory"), msg);
525 if (pimpl_->session_)
526 pimpl_->session_->writeFile();
527 pimpl_->session_.reset();
528 pimpl_->lyx_server_.reset();
529 pimpl_->lyx_socket_.reset();
532 // Kill the application object before exiting. This avoids crashes
533 // when exiting on Linux.
534 if (pimpl_->application_)
535 pimpl_->application_.reset();
539 void LyX::earlyExit(int status)
541 BOOST_ASSERT(pimpl_->application_.get());
542 // LyX::pimpl_::application_ is not initialised at this
543 // point so it's safe to just exit after some cleanup.
549 int LyX::init(int & argc, char * argv[])
551 // check for any spurious extra arguments
552 // other than documents
553 for (int argi = 1; argi < argc ; ++argi) {
554 if (argv[argi][0] == '-') {
556 bformat(_("Wrong command line option `%1$s'. Exiting."),
557 from_utf8(argv[argi]))) << endl;
562 // Initialization of LyX (reads lyxrc and more)
563 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
564 bool success = init();
565 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
569 for (int argi = argc - 1; argi >= 1; --argi) {
570 // get absolute path of file and add ".lyx" to
571 // the filename if necessary
572 pimpl_->files_to_load_.push_back(fileSearch(string(),
573 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
574 "lyx", support::allow_unreadable));
578 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
584 void LyX::addFileToLoad(FileName const & fname)
586 vector<FileName>::const_iterator cit = std::find(
587 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
590 if (cit == pimpl_->files_to_load_.end())
591 pimpl_->files_to_load_.push_back(fname);
595 void LyX::loadFiles()
597 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
598 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
600 for (; it != end; ++it) {
604 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
605 if (buf->loadLyXFile(*it)) {
606 ErrorList const & el = buf->errorList("Parse");
608 for_each(el.begin(), el.end(),
609 boost::bind(&LyX::printError, this, _1));
612 pimpl_->buffer_list_.release(buf);
617 void LyX::execBatchCommands()
619 // The advantage of doing this here is that the event loop
620 // is already started. So any need for interaction will be
624 // if reconfiguration is needed.
625 if (textclasslist.empty()) {
626 switch (Alert::prompt(
627 _("No textclass is found"),
628 _("LyX cannot continue because no textclass is found. "
629 "You can either reconfigure normally, or reconfigure using "
630 "default textclasses, or quit LyX."),
637 // regular reconfigure
638 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
641 // reconfigure --without-latex-config
642 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
643 " --without-latex-config"));
646 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
650 // Execute batch commands if available
651 if (batch_command.empty())
654 LYXERR(Debug::INIT) << "About to handle -x '"
655 << batch_command << '\'' << endl;
657 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
661 void LyX::restoreGuiSession()
663 LyXView * view = newLyXView();
665 // if there is no valid class list, do not load any file.
666 if (textclasslist.empty())
669 // if some files were specified at command-line we assume that the
670 // user wants to edit *these* files and not to restore the session.
671 if (!pimpl_->files_to_load_.empty()) {
672 for_each(pimpl_->files_to_load_.begin(),
673 pimpl_->files_to_load_.end(),
674 bind(&LyXView::loadLyXFile, view, _1, true));
675 // clear this list to save a few bytes of RAM
676 pimpl_->files_to_load_.clear();
677 pimpl_->session_->lastOpened().clear();
679 } else if (lyxrc.load_session) {
680 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
681 // do not add to the lastfile list since these files are restored from
682 // last session, and should be already there (regular files), or should
683 // not be added at all (help files).
684 for_each(lastopened.begin(), lastopened.end(),
685 bind(&LyXView::loadLyXFile, view, _1, false));
687 // clear this list to save a few bytes of RAM
688 pimpl_->session_->lastOpened().clear();
691 BufferList::iterator I = pimpl_->buffer_list_.begin();
692 BufferList::iterator end = pimpl_->buffer_list_.end();
693 for (; I != end; ++I) {
695 if (buf != buf->masterBuffer())
700 // FIXME: Switch to the last loaded Buffer. This must not be the first one
701 // because the Buffer won't be connected in this case. The correct solution
702 // would be to avoid the manual connection of the current Buffer in LyXView.
703 if (!pimpl_->buffer_list_.empty())
704 view->setBuffer(pimpl_->buffer_list_.last());
708 LyXView * LyX::newLyXView()
713 // determine windows size and position, from lyxrc and/or session
715 unsigned int width = 690;
716 unsigned int height = 510;
717 // default icon size, will be overwritten by stored session value
718 unsigned int iconSizeXY = 0;
719 int maximized = LyXView::NotMaximized;
721 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
722 width = lyxrc.geometry_width;
723 height = lyxrc.geometry_height;
725 // if lyxrc returns (0,0), then use session info
727 string val = session().sessionInfo().load("WindowWidth");
729 width = convert<unsigned int>(val);
730 val = session().sessionInfo().load("WindowHeight");
732 height = convert<unsigned int>(val);
733 val = session().sessionInfo().load("WindowMaximized");
735 maximized = convert<int>(val);
736 val = session().sessionInfo().load("IconSizeXY");
738 iconSizeXY = convert<unsigned int>(val);
741 // if user wants to restore window position
744 if (lyxrc.geometry_xysaved) {
745 string val = session().sessionInfo().load("WindowPosX");
747 posx = convert<int>(val);
748 val = session().sessionInfo().load("WindowPosY");
750 posy = convert<int>(val);
753 if (!geometryArg.empty())
759 // create the main window
760 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
768 The SIGHUP signal does not exist on Windows and does not need to be handled.
770 Windows handles SIGFPE and SIGSEGV signals as expected.
772 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
773 cause a new thread to be spawned. This may well result in unexpected
774 behaviour by the single-threaded LyX.
776 SIGTERM signals will come only from another process actually sending
777 that signal using 'raise' in Windows' POSIX compatability layer. It will
778 not come from the general "terminate process" methods that everyone
779 actually uses (and which can't be trapped). Killing an app 'politely' on
780 Windows involves first sending a WM_CLOSE message, something that is
781 caught already by the Qt frontend.
783 For more information see:
785 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
786 ...signals are mostly useless on Windows for a variety of reasons that are
789 'UNIX Application Migration Guide, Chapter 9'
790 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
792 'How To Terminate an Application "Cleanly" in Win32'
793 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
797 static void error_handler(int err_sig)
799 // Throw away any signals other than the first one received.
800 static sig_atomic_t handling_error = false;
803 handling_error = true;
805 // We have received a signal indicating a fatal error, so
806 // try and save the data ASAP.
807 LyX::cref().emergencyCleanup();
809 // These lyxerr calls may or may not work:
811 // Signals are asynchronous, so the main program may be in a very
812 // fragile state when a signal is processed and thus while a signal
813 // handler function executes.
814 // In general, therefore, we should avoid performing any
815 // I/O operations or calling most library and system functions from
818 // This shouldn't matter here, however, as we've already invoked
823 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
827 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
830 lyxerr << "\nlyx: SIGSEGV signal caught\n"
831 "Sorry, you have found a bug in LyX. "
832 "Please read the bug-reporting instructions "
833 "in Help->Introduction and send us a bug report, "
834 "if necessary. Thanks !\nBye." << endl;
842 // Deinstall the signal handlers
844 signal(SIGHUP, SIG_DFL);
846 signal(SIGINT, SIG_DFL);
847 signal(SIGFPE, SIG_DFL);
848 signal(SIGSEGV, SIG_DFL);
849 signal(SIGTERM, SIG_DFL);
852 if (err_sig == SIGSEGV ||
853 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
855 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
864 void LyX::printError(ErrorItem const & ei)
866 docstring tmp = _("LyX: ") + ei.error + char_type(':')
868 std::cerr << to_utf8(tmp) << std::endl;
872 void LyX::initGuiFont()
874 if (lyxrc.roman_font_name.empty())
875 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
877 if (lyxrc.sans_font_name.empty())
878 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
880 if (lyxrc.typewriter_font_name.empty())
881 lyxrc.typewriter_font_name
882 = pimpl_->application_->typewriterFontName();
889 signal(SIGHUP, error_handler);
891 signal(SIGFPE, error_handler);
892 signal(SIGSEGV, error_handler);
893 signal(SIGINT, error_handler);
894 signal(SIGTERM, error_handler);
895 // SIGPIPE can be safely ignored.
897 lyxrc.tempdir_path = package().temp_dir().absFilename();
898 lyxrc.document_path = package().document_dir().absFilename();
900 if (lyxrc.template_path.empty()) {
901 lyxrc.template_path = addPath(package().system_support().absFilename(),
906 // Read configuration files
909 // This one may have been distributed along with LyX.
910 if (!readRcFile("lyxrc.dist"))
913 // Set the language defined by the distributor.
914 //setGuiLanguage(lyxrc.gui_language);
916 // Set the PATH correctly.
917 #if !defined (USE_POSIX_PACKAGING)
918 // Add the directory containing the LyX executable to the path
919 // so that LyX can find things like tex2lyx.
920 if (package().build_support().empty())
921 prependEnvPath("PATH", package().binary_dir().absFilename());
923 if (!lyxrc.path_prefix.empty())
924 prependEnvPath("PATH", lyxrc.path_prefix);
926 // Check that user LyX directory is ok.
927 if (queryUserLyXDir(package().explicit_user_support()))
928 reconfigureUserLyXDir();
930 // no need for a splash when there is no GUI
935 // This one is generated in user_support directory by lib/configure.py.
936 if (!readRcFile("lyxrc.defaults"))
939 // Query the OS to know what formats are viewed natively
940 formats.setAutoOpen();
942 // Read lyxrc.dist again to be able to override viewer auto-detection.
943 readRcFile("lyxrc.dist");
945 system_lyxrc = lyxrc;
946 system_formats = formats;
947 pimpl_->system_converters_ = pimpl_->converters_;
948 pimpl_->system_movers_ = pimpl_->movers_;
949 system_lcolor = lcolor;
951 // This one is edited through the preferences dialog.
952 if (!readRcFile("preferences"))
955 if (!readEncodingsFile("encodings", "unicodesymbols"))
957 if (!readLanguagesFile("languages"))
961 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
967 // read keymap and ui files in batch mode as well
968 // because InsetInfo needs to know these to produce
969 // the correct output
971 // Set the language defined by the user.
972 //setGuiLanguage(lyxrc.gui_language);
974 // Set up command definitions
975 pimpl_->toplevel_cmddef_.reset(new CmdDef);
976 pimpl_->toplevel_cmddef_->read(lyxrc.def_file);
979 pimpl_->toplevel_keymap_.reset(new KeyMap);
980 pimpl_->toplevel_keymap_->read("site");
981 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
982 // load user bind file user.bind
983 pimpl_->toplevel_keymap_->read("user");
985 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
988 if (!readUIFile(lyxrc.ui_file))
991 if (lyxerr.debugging(Debug::LYXRC))
994 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
995 if (!lyxrc.path_prefix.empty())
996 prependEnvPath("PATH", lyxrc.path_prefix);
998 FileName const document_path(lyxrc.document_path);
999 if (document_path.exists() && document_path.isDirectory())
1000 package().document_dir() = document_path;
1002 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
1003 if (package().temp_dir().empty()) {
1004 Alert::error(_("Could not create temporary directory"),
1005 bformat(_("Could not create a temporary directory in\n"
1006 "%1$s. Make sure that this\n"
1007 "path exists and is writable and try again."),
1008 from_utf8(lyxrc.tempdir_path)));
1009 // createLyXTmpDir() tries sufficiently hard to create a
1010 // usable temp dir, so the probability to come here is
1011 // close to zero. We therefore don't try to overcome this
1012 // problem with e.g. asking the user for a new path and
1013 // trying again but simply exit.
1017 LYXERR(Debug::INIT) << "LyX tmp dir: `"
1018 << package().temp_dir().absFilename()
1021 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
1022 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1024 // This must happen after package initialization and after lyxrc is
1025 // read, therefore it can't be done by a static object.
1026 ConverterCache::init();
1032 void LyX::emergencyCleanup() const
1034 // what to do about tmpfiles is non-obvious. we would
1035 // like to delete any we find, but our lyxdir might
1036 // contain documents etc. which might be helpful on
1039 pimpl_->buffer_list_.emergencyWriteAll();
1041 if (pimpl_->lyx_server_)
1042 pimpl_->lyx_server_->emergencyCleanup();
1043 pimpl_->lyx_server_.reset();
1044 pimpl_->lyx_socket_.reset();
1049 void LyX::deadKeyBindings(KeyMap * kbmap)
1051 // bindKeyings for transparent handling of deadkeys
1052 // The keysyms are gotten from XFree86 X11R6
1053 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1054 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1055 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1056 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1057 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1058 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1059 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1060 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1061 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1062 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1063 // nothing with this name
1064 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1065 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1066 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1067 // nothing with this name either...
1068 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1069 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1070 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1071 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1075 // return true if file does not exist or is older than configure.py.
1076 static bool needsUpdate(string const & file)
1078 // We cannot initialize configure_script directly because the package
1079 // is not initialized yet when static objects are constructed.
1080 static FileName configure_script;
1081 static bool firstrun = true;
1084 FileName(addName(package().system_support().absFilename(),
1090 FileName(addName(package().user_support().absFilename(), file));
1091 return !absfile.exists()
1092 || configure_script.lastModified() > absfile.lastModified();
1096 bool LyX::queryUserLyXDir(bool explicit_userdir)
1098 // Does user directory exist?
1099 FileName const sup = package().user_support();
1100 if (sup.exists() && sup.isDirectory()) {
1101 first_start = false;
1103 return needsUpdate("lyxrc.defaults")
1104 || needsUpdate("lyxmodules.lst")
1105 || needsUpdate("textclass.lst")
1106 || needsUpdate("packages.lst");
1109 first_start = !explicit_userdir;
1111 // If the user specified explicitly a directory, ask whether
1112 // to create it. If the user says "no", then exit.
1113 if (explicit_userdir &&
1115 _("Missing user LyX directory"),
1116 bformat(_("You have specified a non-existent user "
1117 "LyX directory, %1$s.\n"
1118 "It is needed to keep your own configuration."),
1119 from_utf8(package().user_support().absFilename())),
1121 _("&Create directory"),
1123 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1124 earlyExit(EXIT_FAILURE);
1127 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1128 from_utf8(sup.absFilename()))) << endl;
1130 if (!sup.createDirectory(0755)) {
1131 // Failed, so let's exit.
1132 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1134 earlyExit(EXIT_FAILURE);
1141 bool LyX::readRcFile(string const & name)
1143 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1145 FileName const lyxrc_path = libFileSearch(string(), name);
1146 if (!lyxrc_path.empty()) {
1148 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1150 if (lyxrc.read(lyxrc_path) < 0) {
1151 showFileError(name);
1155 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1161 // Read the ui file `name'
1162 bool LyX::readUIFile(string const & name, bool include)
1172 struct keyword_item uitags[ui_last - 1] = {
1173 { "include", ui_include },
1174 { "menuset", ui_menuset },
1175 { "toolbars", ui_toolbars },
1176 { "toolbarset", ui_toolbarset }
1179 // Ensure that a file is read only once (prevents include loops)
1180 static std::list<string> uifiles;
1181 std::list<string>::const_iterator it = uifiles.begin();
1182 std::list<string>::const_iterator end = uifiles.end();
1183 it = std::find(it, end, name);
1185 LYXERR(Debug::INIT) << "UI file '" << name
1186 << "' has been read already. "
1187 << "Is this an include loop?"
1192 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1197 ui_path = libFileSearch("ui", name, "inc");
1198 if (ui_path.empty())
1199 ui_path = libFileSearch("ui",
1200 changeExtension(name, "inc"));
1203 ui_path = libFileSearch("ui", name, "ui");
1205 if (ui_path.empty()) {
1206 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1207 showFileError(name);
1211 uifiles.push_back(name);
1213 LYXERR(Debug::INIT) << "Found " << name
1214 << " in " << ui_path << endl;
1215 Lexer lex(uitags, ui_last - 1);
1216 lex.setFile(ui_path);
1218 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1222 if (lyxerr.debugging(Debug::PARSER))
1223 lex.printTable(lyxerr);
1225 while (lex.isOK()) {
1226 switch (lex.lex()) {
1229 string const file = lex.getString();
1230 if (!readUIFile(file, true))
1235 menubackend.read(lex);
1239 toolbarbackend.readToolbars(lex);
1243 toolbarbackend.readToolbarSettings(lex);
1247 if (!rtrim(lex.getString()).empty())
1248 lex.printError("LyX::ReadUIFile: "
1249 "Unknown menu tag: `$$Token'");
1257 // Read the languages file `name'
1258 bool LyX::readLanguagesFile(string const & name)
1260 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1262 FileName const lang_path = libFileSearch(string(), name);
1263 if (lang_path.empty()) {
1264 showFileError(name);
1267 languages.read(lang_path);
1272 // Read the encodings file `name'
1273 bool LyX::readEncodingsFile(string const & enc_name,
1274 string const & symbols_name)
1276 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1277 << symbols_name << "..." << endl;
1279 FileName const symbols_path = libFileSearch(string(), symbols_name);
1280 if (symbols_path.empty()) {
1281 showFileError(symbols_name);
1285 FileName const enc_path = libFileSearch(string(), enc_name);
1286 if (enc_path.empty()) {
1287 showFileError(enc_name);
1290 encodings.read(enc_path, symbols_path);
1299 /// return the the number of arguments consumed
1300 typedef boost::function<int(string const &, string const &)> cmd_helper;
1302 int parse_dbg(string const & arg, string const &)
1305 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1306 Debug::showTags(lyxerr);
1309 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1311 lyxerr.level(Debug::value(arg));
1312 Debug::showLevel(lyxerr, lyxerr.level());
1317 int parse_help(string const &, string const &)
1320 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1321 "Command line switches (case sensitive):\n"
1322 "\t-help summarize LyX usage\n"
1323 "\t-userdir dir set user directory to dir\n"
1324 "\t-sysdir dir set system directory to dir\n"
1325 "\t-geometry WxH+X+Y set geometry of the main window\n"
1326 "\t-dbg feature[,feature]...\n"
1327 " select the features to debug.\n"
1328 " Type `lyx -dbg' to see the list of features\n"
1329 "\t-x [--execute] command\n"
1330 " where command is a lyx command.\n"
1331 "\t-e [--export] fmt\n"
1332 " where fmt is the export format of choice.\n"
1333 "\t-i [--import] fmt file.xxx\n"
1334 " where fmt is the import format of choice\n"
1335 " and file.xxx is the file to be imported.\n"
1336 "\t-version summarize version and build info\n"
1337 "Check the LyX man page for more details.")) << endl;
1342 int parse_version(string const &, string const &)
1344 lyxerr << "LyX " << lyx_version
1345 << " (" << lyx_release_date << ")" << endl;
1346 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1348 lyxerr << lyx_version_info << endl;
1353 int parse_sysdir(string const & arg, string const &)
1356 Alert::error(_("No system directory"),
1357 _("Missing directory for -sysdir switch"));
1360 cl_system_support = arg;
1364 int parse_userdir(string const & arg, string const &)
1367 Alert::error(_("No user directory"),
1368 _("Missing directory for -userdir switch"));
1371 cl_user_support = arg;
1375 int parse_execute(string const & arg, string const &)
1378 Alert::error(_("Incomplete command"),
1379 _("Missing command string after --execute switch"));
1386 int parse_export(string const & type, string const &)
1389 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1390 "--export switch")) << endl;
1393 batch = "buffer-export " + type;
1398 int parse_import(string const & type, string const & file)
1401 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1402 "--import switch")) << endl;
1406 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1410 batch = "buffer-import " + type + ' ' + file;
1414 int parse_geometry(string const & arg1, string const &)
1417 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1418 // remove also the arg
1421 // don't remove "-geometry"
1430 void LyX::easyParse(int & argc, char * argv[])
1432 std::map<string, cmd_helper> cmdmap;
1434 cmdmap["-dbg"] = parse_dbg;
1435 cmdmap["-help"] = parse_help;
1436 cmdmap["--help"] = parse_help;
1437 cmdmap["-version"] = parse_version;
1438 cmdmap["--version"] = parse_version;
1439 cmdmap["-sysdir"] = parse_sysdir;
1440 cmdmap["-userdir"] = parse_userdir;
1441 cmdmap["-x"] = parse_execute;
1442 cmdmap["--execute"] = parse_execute;
1443 cmdmap["-e"] = parse_export;
1444 cmdmap["--export"] = parse_export;
1445 cmdmap["-i"] = parse_import;
1446 cmdmap["--import"] = parse_import;
1447 cmdmap["-geometry"] = parse_geometry;
1449 for (int i = 1; i < argc; ++i) {
1450 std::map<string, cmd_helper>::const_iterator it
1451 = cmdmap.find(argv[i]);
1453 // don't complain if not found - may be parsed later
1454 if (it == cmdmap.end())
1457 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1458 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1460 int const remove = 1 + it->second(arg, arg2);
1462 // Now, remove used arguments by shifting
1463 // the following ones remove places down.
1466 for (int j = i; j < argc; ++j)
1467 argv[j] = argv[j + remove];
1472 batch_command = batch;
1476 FuncStatus getStatus(FuncRequest const & action)
1478 return LyX::ref().lyxFunc().getStatus(action);
1482 void dispatch(FuncRequest const & action)
1484 LyX::ref().lyxFunc().dispatch(action);
1488 BufferList & theBufferList()
1490 return LyX::ref().bufferList();
1494 LyXFunc & theLyXFunc()
1496 return LyX::ref().lyxFunc();
1500 Server & theServer()
1502 // FIXME: this should not be use_gui dependent
1503 BOOST_ASSERT(use_gui);
1504 return LyX::ref().server();
1508 ServerSocket & theServerSocket()
1510 // FIXME: this should not be use_gui dependent
1511 BOOST_ASSERT(use_gui);
1512 return LyX::ref().socket();
1516 KeyMap & theTopLevelKeymap()
1518 return LyX::ref().topLevelKeymap();
1522 Converters & theConverters()
1524 return LyX::ref().converters();
1528 Converters & theSystemConverters()
1530 return LyX::ref().systemConverters();
1534 Movers & theMovers()
1536 return LyX::ref().pimpl_->movers_;
1540 Mover const & getMover(std::string const & fmt)
1542 return LyX::ref().pimpl_->movers_(fmt);
1546 void setMover(std::string const & fmt, std::string const & command)
1548 LyX::ref().pimpl_->movers_.set(fmt, command);
1552 Movers & theSystemMovers()
1554 return LyX::ref().pimpl_->system_movers_;
1558 Messages & getMessages(std::string const & language)
1560 return LyX::ref().getMessages(language);
1564 Messages & getGuiMessages()
1566 return LyX::ref().getGuiMessages();