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.
20 #include "ConverterCache.h"
22 #include "buffer_funcs.h"
23 #include "bufferlist.h"
24 #include "converter.h"
27 #include "errorlist.h"
35 #include "LyXAction.h"
39 #include "lyxserver.h"
40 #include "lyxsocket.h"
41 #include "lyxtextclasslist.h"
42 #include "MenuBackend.h"
44 #include "ToolbarBackend.h"
46 #include "frontends/Alert.h"
47 #include "frontends/Application.h"
48 #include "frontends/Gui.h"
49 #include "frontends/LyXView.h"
51 #include "support/environment.h"
52 #include "support/filetools.h"
53 #include "support/fontutils.h"
54 #include "support/lyxlib.h"
55 #include "support/convert.h"
56 #include "support/os.h"
57 #include "support/package.h"
58 #include "support/path.h"
59 #include "support/systemcall.h"
60 #include "support/unicode.h"
62 #include <boost/bind.hpp>
63 #include <boost/filesystem/operations.hpp>
72 using support::addName;
73 using support::addPath;
74 using support::bformat;
75 using support::createDirectory;
76 using support::createLyXTmpDir;
77 using support::destroyDir;
78 using support::FileName;
79 using support::fileSearch;
80 using support::getEnv;
81 using support::i18nLibFileSearch;
82 using support::libFileSearch;
83 using support::package;
84 using support::prependEnvPath;
86 using support::Systemcall;
88 namespace Alert = frontend::Alert;
89 namespace os = support::os;
90 namespace fs = boost::filesystem;
97 #ifndef CXX_GLOBAL_CSTD
104 /// are we using the GUI at all?
106 * We default to true and this is changed to false when the export feature is used.
113 /// Don't try to remove the temporary directory if it has not been created
114 bool remove_tmpdir = false;
116 // Filled with the command line arguments "foo" of "-sysdir foo" or
118 string cl_system_support;
119 string cl_user_support;
121 std::string geometryArg;
123 LyX * singleton_ = 0;
125 void showFileError(string const & error)
127 Alert::warning(_("Could not read configuration file"),
128 bformat(_("Error while reading the configuration file\n%1$s.\n"
129 "Please check your installation."), from_utf8(error)));
133 void reconfigureUserLyXDir()
135 string const configure_command = package().configure_command();
137 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
138 support::Path p(package().user_support());
140 one.startscript(Systemcall::Wait, configure_command);
141 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
147 /// The main application class private implementation.
148 struct LyX::Singletons
150 Singletons(): iconv(ucs4_codeset, "UTF-8")
153 /// our function handler
156 BufferList buffer_list_;
158 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
160 boost::scoped_ptr<LyXServer> lyx_server_;
162 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
164 boost::scoped_ptr<frontend::Application> application_;
165 /// lyx session, containing lastfiles, lastfilepos, and lastopened
166 boost::scoped_ptr<Session> session_;
169 IconvProcessor iconv;
171 /// Files to load at start.
172 vector<FileName> files_to_load_;
176 frontend::Application * theApp()
179 return &singleton_->application();
192 BOOST_ASSERT(singleton_);
197 LyX const & LyX::cref()
199 BOOST_ASSERT(singleton_);
208 pimpl_.reset(new Singletons);
213 BufferList & LyX::bufferList()
215 return pimpl_->buffer_list_;
219 BufferList const & LyX::bufferList() const
221 return pimpl_->buffer_list_;
225 Session & LyX::session()
227 BOOST_ASSERT(pimpl_->session_.get());
228 return *pimpl_->session_.get();
232 Session const & LyX::session() const
234 BOOST_ASSERT(pimpl_->session_.get());
235 return *pimpl_->session_.get();
239 LyXFunc & LyX::lyxFunc()
241 return pimpl_->lyxfunc_;
245 LyXFunc const & LyX::lyxFunc() const
247 return pimpl_->lyxfunc_;
251 LyXServer & LyX::server()
253 BOOST_ASSERT(pimpl_->lyx_server_.get());
254 return *pimpl_->lyx_server_.get();
258 LyXServer const & LyX::server() const
260 BOOST_ASSERT(pimpl_->lyx_server_.get());
261 return *pimpl_->lyx_server_.get();
265 LyXServerSocket & LyX::socket()
267 BOOST_ASSERT(pimpl_->lyx_socket_.get());
268 return *pimpl_->lyx_socket_.get();
272 LyXServerSocket const & LyX::socket() const
274 BOOST_ASSERT(pimpl_->lyx_socket_.get());
275 return *pimpl_->lyx_socket_.get();
279 frontend::Application & LyX::application()
281 BOOST_ASSERT(pimpl_->application_.get());
282 return *pimpl_->application_.get();
286 frontend::Application const & LyX::application() const
288 BOOST_ASSERT(pimpl_->application_.get());
289 return *pimpl_->application_.get();
293 kb_keymap & LyX::topLevelKeymap()
295 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
296 return *pimpl_->toplevel_keymap_.get();
300 IconvProcessor & LyX::iconvProcessor()
302 return pimpl_->iconv;
306 kb_keymap const & LyX::topLevelKeymap() const
308 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
309 return *pimpl_->toplevel_keymap_.get();
313 Buffer const * const LyX::updateInset(InsetBase const * inset) const
318 Buffer const * buffer_ptr = 0;
319 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
320 vector<int>::const_iterator it = view_ids.begin();
321 vector<int>::const_iterator const end = view_ids.end();
322 for (; it != end; ++it) {
324 pimpl_->application_->gui().view(*it).updateInset(inset);
332 int LyX::exec(int & argc, char * argv[])
334 // Here we need to parse the command line. At least
335 // we need to parse for "-dbg" and "-help"
336 easyParse(argc, argv);
338 support::init_package(to_utf8(from_local8bit(argv[0])),
339 cl_system_support, cl_user_support,
340 support::top_build_dir_is_one_level_up);
343 // FIXME: create a ConsoleApplication
344 int exit_status = init(argc, argv);
352 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
357 BufferList::iterator begin = pimpl_->buffer_list_.begin();
358 BufferList::iterator end = pimpl_->buffer_list_.end();
360 bool final_success = false;
361 for (BufferList::iterator I = begin; I != end; ++I) {
363 bool success = false;
364 buf->dispatch(batch_command, &success);
365 final_success |= success;
368 return !final_success;
371 // Force adding of font path _before_ Application is initialized
372 support::addFontResources();
374 // Let the frontend parse and remove all arguments that it knows
375 pimpl_->application_.reset(createApplication(argc, argv));
379 // Parse and remove all known arguments in the LyX singleton
380 // Give an error for all remaining ones.
381 int exit_status = init(argc, argv);
383 // Kill the application object before exiting.
384 pimpl_->application_.reset();
391 /* Create a CoreApplication class that will provide the main event loop
392 * and the socket callback registering. With Qt4, only QtCore
393 * library would be needed.
394 * When this is done, a server_mode could be created and the following two
395 * line would be moved out from here.
397 // Note: socket callback must be registered after init(argc, argv)
398 // such that package().temp_dir() is properly initialized.
399 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
400 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
401 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
403 // Start the real execution loop.
404 exit_status = pimpl_->application_->exec();
408 // Restore original font resources after Application is destroyed.
409 support::restoreFontResources();
415 void LyX::prepareExit()
417 // Set a flag that we do quitting from the program,
418 // so no refreshes are necessary.
421 // close buffers first
422 pimpl_->buffer_list_.closeAll();
424 // do any other cleanup procedures now
426 lyxerr[Debug::INFO] << "Deleting tmp dir "
427 << package().temp_dir() << endl;
429 if (!destroyDir(FileName(package().temp_dir()))) {
430 docstring const msg =
431 bformat(_("Unable to remove the temporary directory %1$s"),
432 from_utf8(package().temp_dir()));
433 Alert::warning(_("Unable to remove temporary directory"), msg);
438 if (pimpl_->session_)
439 pimpl_->session_->writeFile();
440 pimpl_->session_.reset();
441 pimpl_->lyx_server_.reset();
442 pimpl_->lyx_socket_.reset();
445 // Kill the application object before exiting. This avoids crashes
446 // when exiting on Linux.
447 if (pimpl_->application_)
448 pimpl_->application_.reset();
452 void LyX::earlyExit(int status)
454 BOOST_ASSERT(pimpl_->application_.get());
455 // LyX::pimpl_::application_ is not initialised at this
456 // point so it's safe to just exit after some cleanup.
462 int LyX::init(int & argc, char * argv[])
464 // check for any spurious extra arguments
465 // other than documents
466 for (int argi = 1; argi < argc ; ++argi) {
467 if (argv[argi][0] == '-') {
469 bformat(_("Wrong command line option `%1$s'. Exiting."),
470 from_utf8(argv[argi]))) << endl;
475 // Initialization of LyX (reads lyxrc and more)
476 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
477 bool success = init();
478 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
482 for (int argi = argc - 1; argi >= 1; --argi) {
483 // get absolute path of file and add ".lyx" to
484 // the filename if necessary
485 pimpl_->files_to_load_.push_back(fileSearch(string(),
486 os::internal_path(to_utf8(from_local8bit(argv[argi]))), "lyx"));
490 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
496 void LyX::loadFiles()
498 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
499 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
501 for (; it != end; ++it) {
505 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
506 if (loadLyXFile(buf, *it)) {
507 ErrorList const & el = buf->errorList("Parse");
509 for_each(el.begin(), el.end(),
510 boost::bind(&LyX::printError, this, _1));
513 pimpl_->buffer_list_.release(buf);
518 void LyX::execBatchCommands()
520 // The advantage of doing this here is that the event loop
521 // is already started. So any need for interaction will be
525 // Execute batch commands if available
526 if (batch_command.empty())
529 lyxerr[Debug::INIT] << "About to handle -x '"
530 << batch_command << '\'' << endl;
532 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
536 void LyX::restoreGuiSession()
538 LyXView * view = newLyXView();
540 // if some files were specified at command-line we assume that the
541 // user wants to edit *these* files and not to restore the session.
542 if (!pimpl_->files_to_load_.empty()) {
543 for_each(pimpl_->files_to_load_.begin(),
544 pimpl_->files_to_load_.end(),
545 bind(&LyXView::loadLyXFile, view, _1, true));
546 // clear this list to save a few bytes of RAM
547 pimpl_->files_to_load_.clear();
548 pimpl_->session_->lastOpened().clear();
552 if (!lyxrc.load_session)
555 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
556 // do not add to the lastfile list since these files are restored from
557 // last session, and should be already there (regular files), or should
558 // not be added at all (help files).
559 for_each(lastopened.begin(), lastopened.end(),
560 bind(&LyXView::loadLyXFile, view, _1, false));
562 // clear this list to save a few bytes of RAM
563 pimpl_->session_->lastOpened().clear();
567 LyXView * LyX::newLyXView()
572 // determine windows size and position, from lyxrc and/or session
574 unsigned int width = 690;
575 unsigned int height = 510;
576 // default icon size, will be overwritten by stored session value
577 unsigned int iconSizeXY = 0;
578 bool maximize = false;
580 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
581 width = lyxrc.geometry_width;
582 height = lyxrc.geometry_height;
584 // if lyxrc returns (0,0), then use session info
586 string val = session().sessionInfo().load("WindowWidth");
588 width = convert<unsigned int>(val);
589 val = session().sessionInfo().load("WindowHeight");
591 height = convert<unsigned int>(val);
592 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
594 val = session().sessionInfo().load("IconSizeXY");
596 iconSizeXY = convert<unsigned int>(val);
599 // if user wants to restore window position
602 if (lyxrc.geometry_xysaved) {
603 string val = session().sessionInfo().load("WindowPosX");
605 posx = convert<int>(val);
606 val = session().sessionInfo().load("WindowPosY");
608 posy = convert<int>(val);
611 if (!geometryArg.empty())
617 // create the main window
618 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
626 The SIGHUP signal does not exist on Windows and does not need to be handled.
628 Windows handles SIGFPE and SIGSEGV signals as expected.
630 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
631 cause a new thread to be spawned. This may well result in unexpected
632 behaviour by the single-threaded LyX.
634 SIGTERM signals will come only from another process actually sending
635 that signal using 'raise' in Windows' POSIX compatability layer. It will
636 not come from the general "terminate process" methods that everyone
637 actually uses (and which can't be trapped). Killing an app 'politely' on
638 Windows involves first sending a WM_CLOSE message, something that is
639 caught already by the Qt frontend.
641 For more information see:
643 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
644 ...signals are mostly useless on Windows for a variety of reasons that are
647 'UNIX Application Migration Guide, Chapter 9'
648 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
650 'How To Terminate an Application "Cleanly" in Win32'
651 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
655 static void error_handler(int err_sig)
657 // Throw away any signals other than the first one received.
658 static sig_atomic_t handling_error = false;
661 handling_error = true;
663 // We have received a signal indicating a fatal error, so
664 // try and save the data ASAP.
665 LyX::cref().emergencyCleanup();
667 // These lyxerr calls may or may not work:
669 // Signals are asynchronous, so the main program may be in a very
670 // fragile state when a signal is processed and thus while a signal
671 // handler function executes.
672 // In general, therefore, we should avoid performing any
673 // I/O operations or calling most library and system functions from
676 // This shouldn't matter here, however, as we've already invoked
681 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
685 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
688 lyxerr << "\nlyx: SIGSEGV signal caught\n"
689 "Sorry, you have found a bug in LyX. "
690 "Please read the bug-reporting instructions "
691 "in Help->Introduction and send us a bug report, "
692 "if necessary. Thanks !\nBye." << endl;
700 // Deinstall the signal handlers
702 signal(SIGHUP, SIG_DFL);
704 signal(SIGINT, SIG_DFL);
705 signal(SIGFPE, SIG_DFL);
706 signal(SIGSEGV, SIG_DFL);
707 signal(SIGTERM, SIG_DFL);
710 if (err_sig == SIGSEGV ||
711 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
713 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
722 void LyX::printError(ErrorItem const & ei)
724 docstring tmp = _("LyX: ") + ei.error + char_type(':')
726 std::cerr << to_utf8(tmp) << std::endl;
730 void LyX::initGuiFont()
732 if (lyxrc.roman_font_name.empty())
733 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
735 if (lyxrc.sans_font_name.empty())
736 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
738 if (lyxrc.typewriter_font_name.empty())
739 lyxrc.typewriter_font_name
740 = pimpl_->application_->typewriterFontName();
747 signal(SIGHUP, error_handler);
749 signal(SIGFPE, error_handler);
750 signal(SIGSEGV, error_handler);
751 signal(SIGINT, error_handler);
752 signal(SIGTERM, error_handler);
753 // SIGPIPE can be safely ignored.
755 lyxrc.tempdir_path = package().temp_dir();
756 lyxrc.document_path = package().document_dir();
758 if (lyxrc.template_path.empty()) {
759 lyxrc.template_path = addPath(package().system_support(),
764 // Read configuration files
767 // This one may have been distributed along with LyX.
768 if (!readRcFile("lyxrc.dist"))
771 // Set the PATH correctly.
772 #if !defined (USE_POSIX_PACKAGING)
773 // Add the directory containing the LyX executable to the path
774 // so that LyX can find things like tex2lyx.
775 if (package().build_support().empty())
776 prependEnvPath("PATH", package().binary_dir());
778 if (!lyxrc.path_prefix.empty())
779 prependEnvPath("PATH", lyxrc.path_prefix);
781 // Check that user LyX directory is ok.
782 if (queryUserLyXDir(package().explicit_user_support()))
783 reconfigureUserLyXDir();
785 // no need for a splash when there is no GUI
790 // This one is generated in user_support directory by lib/configure.py.
791 if (!readRcFile("lyxrc.defaults"))
794 // Query the OS to know what formats are viewed natively
795 formats.setAutoOpen();
797 // Read lyxrc.dist again to be able to override viewer auto-detection.
798 readRcFile("lyxrc.dist");
800 system_lyxrc = lyxrc;
801 system_formats = formats;
802 system_converters = converters;
803 system_movers = movers;
804 system_lcolor = lcolor;
806 // This one is edited through the preferences dialog.
807 if (!readRcFile("preferences"))
810 if (!readEncodingsFile("encodings"))
812 if (!readLanguagesFile("languages"))
816 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
822 pimpl_->toplevel_keymap_.reset(new kb_keymap);
823 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
824 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
826 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
829 if (!readUIFile(lyxrc.ui_file))
833 if (lyxerr.debugging(Debug::LYXRC))
836 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
837 if (!lyxrc.path_prefix.empty())
838 prependEnvPath("PATH", lyxrc.path_prefix);
840 FileName const document_path(lyxrc.document_path);
841 if (fs::exists(document_path.toFilesystemEncoding()) &&
842 fs::is_directory(document_path.toFilesystemEncoding()))
843 package().document_dir() = lyxrc.document_path;
845 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
846 if (package().temp_dir().empty()) {
847 Alert::error(_("Could not create temporary directory"),
848 bformat(_("Could not create a temporary directory in\n"
849 "%1$s. Make sure that this\n"
850 "path exists and is writable and try again."),
851 from_utf8(lyxrc.tempdir_path)));
852 // createLyXTmpDir() tries sufficiently hard to create a
853 // usable temp dir, so the probability to come here is
854 // close to zero. We therefore don't try to overcome this
855 // problem with e.g. asking the user for a new path and
856 // trying again but simply exit.
859 remove_tmpdir = true;
861 if (lyxerr.debugging(Debug::INIT)) {
862 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
865 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
866 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
868 // This must happen after package initialization and after lyxrc is
869 // read, therefore it can't be done by a static object.
870 ConverterCache::init();
876 void LyX::defaultKeyBindings(kb_keymap * kbmap)
878 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
879 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
880 kbmap->bind("Up", FuncRequest(LFUN_UP));
881 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
883 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
884 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
885 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
886 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
888 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
889 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
890 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
891 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
893 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
894 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
896 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
897 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
899 // kbmap->bindings to enable the use of the numeric keypad
901 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
902 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
903 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
904 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
906 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
915 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
916 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
917 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
918 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
919 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
920 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
921 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
922 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
923 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
924 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
928 void LyX::emergencyCleanup() const
930 // what to do about tmpfiles is non-obvious. we would
931 // like to delete any we find, but our lyxdir might
932 // contain documents etc. which might be helpful on
935 pimpl_->buffer_list_.emergencyWriteAll();
937 if (pimpl_->lyx_server_)
938 pimpl_->lyx_server_->emergencyCleanup();
939 pimpl_->lyx_server_.reset();
940 pimpl_->lyx_socket_.reset();
945 void LyX::deadKeyBindings(kb_keymap * kbmap)
947 // bindKeyings for transparent handling of deadkeys
948 // The keysyms are gotten from XFree86 X11R6
949 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
950 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
951 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
952 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
953 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
954 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
955 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
956 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
957 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
958 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
959 // nothing with this name
960 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
961 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
962 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
963 // nothing with this name either...
964 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
965 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
966 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
967 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
973 // return true if file does not exist or is older than configure.py.
974 bool needsUpdate(string const & file)
976 // We cannot initialize configure_script directly because the package
977 // is not initialized yet when static objects are constructed.
978 static string configure_script;
979 static bool firstrun = true;
981 configure_script = FileName(addName(
982 package().system_support(),
983 "configure.py")).toFilesystemEncoding();
987 string const absfile = FileName(addName(
988 package().user_support(), file)).toFilesystemEncoding();
989 return (! fs::exists(absfile))
990 || (fs::last_write_time(configure_script)
991 > fs::last_write_time(absfile));
997 bool LyX::queryUserLyXDir(bool explicit_userdir)
999 // Does user directory exist?
1000 string const user_support =
1001 FileName(package().user_support()).toFilesystemEncoding();
1002 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1003 first_start = false;
1005 return needsUpdate("lyxrc.defaults")
1006 || needsUpdate("textclass.lst")
1007 || needsUpdate("packages.lst");
1010 first_start = !explicit_userdir;
1012 // If the user specified explicitly a directory, ask whether
1013 // to create it. If the user says "no", then exit.
1014 if (explicit_userdir &&
1016 _("Missing user LyX directory"),
1017 bformat(_("You have specified a non-existent user "
1018 "LyX directory, %1$s.\n"
1019 "It is needed to keep your own configuration."),
1020 from_utf8(package().user_support())),
1022 _("&Create directory"),
1024 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1025 earlyExit(EXIT_FAILURE);
1028 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1029 from_utf8(package().user_support())))
1032 if (!createDirectory(package().user_support(), 0755)) {
1033 // Failed, so let's exit.
1034 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1036 earlyExit(EXIT_FAILURE);
1043 bool LyX::readRcFile(string const & name)
1045 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1047 FileName const lyxrc_path = libFileSearch(string(), name);
1048 if (!lyxrc_path.empty()) {
1050 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1052 if (lyxrc.read(lyxrc_path) < 0) {
1053 showFileError(name);
1057 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1063 // Read the ui file `name'
1064 bool LyX::readUIFile(string const & name)
1074 struct keyword_item uitags[ui_last - 1] = {
1075 { "include", ui_include },
1076 { "menuset", ui_menuset },
1077 { "toolbar", ui_toolbar },
1078 { "toolbars", ui_toolbars }
1081 // Ensure that a file is read only once (prevents include loops)
1082 static std::list<string> uifiles;
1083 std::list<string>::const_iterator it = uifiles.begin();
1084 std::list<string>::const_iterator end = uifiles.end();
1085 it = std::find(it, end, name);
1087 lyxerr[Debug::INIT] << "UI file '" << name
1088 << "' has been read already. "
1089 << "Is this an include loop?"
1094 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1096 FileName const ui_path = libFileSearch("ui", name, "ui");
1098 if (ui_path.empty()) {
1099 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1100 showFileError(name);
1103 uifiles.push_back(name);
1105 lyxerr[Debug::INIT] << "Found " << name
1106 << " in " << ui_path << endl;
1107 LyXLex lex(uitags, ui_last - 1);
1108 lex.setFile(ui_path);
1110 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1114 if (lyxerr.debugging(Debug::PARSER))
1115 lex.printTable(lyxerr);
1117 while (lex.isOK()) {
1118 switch (lex.lex()) {
1121 string const file = lex.getString();
1122 if (!readUIFile(file))
1127 menubackend.read(lex);
1131 toolbarbackend.read(lex);
1135 toolbarbackend.readToolbars(lex);
1139 if (!rtrim(lex.getString()).empty())
1140 lex.printError("LyX::ReadUIFile: "
1141 "Unknown menu tag: `$$Token'");
1149 // Read the languages file `name'
1150 bool LyX::readLanguagesFile(string const & name)
1152 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1154 FileName const lang_path = libFileSearch(string(), name);
1155 if (lang_path.empty()) {
1156 showFileError(name);
1159 languages.read(lang_path);
1164 // Read the encodings file `name'
1165 bool LyX::readEncodingsFile(string const & name)
1167 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1169 FileName const enc_path = libFileSearch(string(), name);
1170 if (enc_path.empty()) {
1171 showFileError(name);
1174 encodings.read(enc_path);
1183 /// return the the number of arguments consumed
1184 typedef boost::function<int(string const &, string const &)> cmd_helper;
1186 int parse_dbg(string const & arg, string const &)
1189 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1190 Debug::showTags(lyxerr);
1193 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1195 lyxerr.level(Debug::value(arg));
1196 Debug::showLevel(lyxerr, lyxerr.level());
1201 int parse_help(string const &, string const &)
1204 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1205 "Command line switches (case sensitive):\n"
1206 "\t-help summarize LyX usage\n"
1207 "\t-userdir dir set user directory to dir\n"
1208 "\t-sysdir dir set system directory to dir\n"
1209 "\t-geometry WxH+X+Y set geometry of the main window\n"
1210 "\t-dbg feature[,feature]...\n"
1211 " select the features to debug.\n"
1212 " Type `lyx -dbg' to see the list of features\n"
1213 "\t-x [--execute] command\n"
1214 " where command is a lyx command.\n"
1215 "\t-e [--export] fmt\n"
1216 " where fmt is the export format of choice.\n"
1217 "\t-i [--import] fmt file.xxx\n"
1218 " where fmt is the import format of choice\n"
1219 " and file.xxx is the file to be imported.\n"
1220 "\t-version summarize version and build info\n"
1221 "Check the LyX man page for more details.")) << endl;
1226 int parse_version(string const &, string const &)
1228 lyxerr << "LyX " << lyx_version
1229 << " (" << lyx_release_date << ")" << endl;
1230 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1232 lyxerr << lyx_version_info << endl;
1237 int parse_sysdir(string const & arg, string const &)
1240 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1243 cl_system_support = arg;
1247 int parse_userdir(string const & arg, string const &)
1250 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1253 cl_user_support = arg;
1257 int parse_execute(string const & arg, string const &)
1260 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1267 int parse_export(string const & type, string const &)
1270 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1271 "--export switch")) << endl;
1274 batch = "buffer-export " + type;
1279 int parse_import(string const & type, string const & file)
1282 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1283 "--import switch")) << endl;
1287 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1291 batch = "buffer-import " + type + ' ' + file;
1295 int parse_geometry(string const & arg1, string const &)
1298 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1299 // remove also the arg
1302 // don't remove "-geometry"
1311 void LyX::easyParse(int & argc, char * argv[])
1313 std::map<string, cmd_helper> cmdmap;
1315 cmdmap["-dbg"] = parse_dbg;
1316 cmdmap["-help"] = parse_help;
1317 cmdmap["--help"] = parse_help;
1318 cmdmap["-version"] = parse_version;
1319 cmdmap["--version"] = parse_version;
1320 cmdmap["-sysdir"] = parse_sysdir;
1321 cmdmap["-userdir"] = parse_userdir;
1322 cmdmap["-x"] = parse_execute;
1323 cmdmap["--execute"] = parse_execute;
1324 cmdmap["-e"] = parse_export;
1325 cmdmap["--export"] = parse_export;
1326 cmdmap["-i"] = parse_import;
1327 cmdmap["--import"] = parse_import;
1328 cmdmap["-geometry"] = parse_geometry;
1330 for (int i = 1; i < argc; ++i) {
1331 std::map<string, cmd_helper>::const_iterator it
1332 = cmdmap.find(argv[i]);
1334 // don't complain if not found - may be parsed later
1335 if (it == cmdmap.end())
1338 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1339 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1341 int const remove = 1 + it->second(arg, arg2);
1343 // Now, remove used arguments by shifting
1344 // the following ones remove places down.
1347 for (int j = i; j < argc; ++j)
1348 argv[j] = argv[j + remove];
1353 batch_command = batch;
1357 FuncStatus getStatus(FuncRequest const & action)
1359 return LyX::ref().lyxFunc().getStatus(action);
1363 void dispatch(FuncRequest const & action)
1365 LyX::ref().lyxFunc().dispatch(action);
1369 BufferList & theBufferList()
1371 return LyX::ref().bufferList();
1375 LyXFunc & theLyXFunc()
1377 return LyX::ref().lyxFunc();
1381 LyXServer & theLyXServer()
1383 // FIXME: this should not be use_gui dependent
1384 BOOST_ASSERT(use_gui);
1385 return LyX::ref().server();
1389 LyXServerSocket & theLyXServerSocket()
1391 // FIXME: this should not be use_gui dependent
1392 BOOST_ASSERT(use_gui);
1393 return LyX::ref().socket();
1397 kb_keymap & theTopLevelKeymap()
1399 BOOST_ASSERT(use_gui);
1400 return LyX::ref().topLevelKeymap();
1404 IconvProcessor & utf8ToUcs4()
1406 return LyX::ref().iconvProcessor();