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 // Filled with the command line arguments "foo" of "-sysdir foo" or
115 string cl_system_support;
116 string cl_user_support;
118 std::string geometryArg;
120 LyX * singleton_ = 0;
122 void showFileError(string const & error)
124 Alert::warning(_("Could not read configuration file"),
125 bformat(_("Error while reading the configuration file\n%1$s.\n"
126 "Please check your installation."), from_utf8(error)));
130 void reconfigureUserLyXDir()
132 string const configure_command = package().configure_command();
134 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
135 support::Path p(package().user_support());
137 one.startscript(Systemcall::Wait, configure_command);
138 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
144 /// The main application class private implementation.
145 struct LyX::Singletons
147 Singletons(): iconv(ucs4_codeset, "UTF-8")
150 /// our function handler
153 BufferList buffer_list_;
155 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
157 boost::scoped_ptr<LyXServer> lyx_server_;
159 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
161 boost::scoped_ptr<frontend::Application> application_;
162 /// lyx session, containing lastfiles, lastfilepos, and lastopened
163 boost::scoped_ptr<Session> session_;
166 IconvProcessor iconv;
168 /// Files to load at start.
169 vector<FileName> files_to_load_;
173 frontend::Application * theApp()
176 return &singleton_->application();
189 BOOST_ASSERT(singleton_);
194 LyX const & LyX::cref()
196 BOOST_ASSERT(singleton_);
205 pimpl_.reset(new Singletons);
210 BufferList & LyX::bufferList()
212 return pimpl_->buffer_list_;
216 BufferList const & LyX::bufferList() const
218 return pimpl_->buffer_list_;
222 Session & LyX::session()
224 BOOST_ASSERT(pimpl_->session_.get());
225 return *pimpl_->session_.get();
229 Session const & LyX::session() const
231 BOOST_ASSERT(pimpl_->session_.get());
232 return *pimpl_->session_.get();
236 LyXFunc & LyX::lyxFunc()
238 return pimpl_->lyxfunc_;
242 LyXFunc const & LyX::lyxFunc() const
244 return pimpl_->lyxfunc_;
248 LyXServer & LyX::server()
250 BOOST_ASSERT(pimpl_->lyx_server_.get());
251 return *pimpl_->lyx_server_.get();
255 LyXServer const & LyX::server() const
257 BOOST_ASSERT(pimpl_->lyx_server_.get());
258 return *pimpl_->lyx_server_.get();
262 LyXServerSocket & LyX::socket()
264 BOOST_ASSERT(pimpl_->lyx_socket_.get());
265 return *pimpl_->lyx_socket_.get();
269 LyXServerSocket const & LyX::socket() const
271 BOOST_ASSERT(pimpl_->lyx_socket_.get());
272 return *pimpl_->lyx_socket_.get();
276 frontend::Application & LyX::application()
278 BOOST_ASSERT(pimpl_->application_.get());
279 return *pimpl_->application_.get();
283 frontend::Application const & LyX::application() const
285 BOOST_ASSERT(pimpl_->application_.get());
286 return *pimpl_->application_.get();
290 kb_keymap & LyX::topLevelKeymap()
292 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
293 return *pimpl_->toplevel_keymap_.get();
297 IconvProcessor & LyX::iconvProcessor()
299 return pimpl_->iconv;
303 kb_keymap const & LyX::topLevelKeymap() const
305 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
306 return *pimpl_->toplevel_keymap_.get();
310 Buffer const * const LyX::updateInset(InsetBase const * inset) const
315 Buffer const * buffer_ptr = 0;
316 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
317 vector<int>::const_iterator it = view_ids.begin();
318 vector<int>::const_iterator const end = view_ids.end();
319 for (; it != end; ++it) {
321 pimpl_->application_->gui().view(*it).updateInset(inset);
329 int LyX::exec(int & argc, char * argv[])
331 // Here we need to parse the command line. At least
332 // we need to parse for "-dbg" and "-help"
333 easyParse(argc, argv);
335 support::init_package(argv[0], cl_system_support, cl_user_support,
336 support::top_build_dir_is_one_level_up);
339 // FIXME: create a ConsoleApplication
340 int exit_status = init(argc, argv);
348 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
353 BufferList::iterator begin = pimpl_->buffer_list_.begin();
354 BufferList::iterator end = pimpl_->buffer_list_.end();
356 bool final_success = false;
357 for (BufferList::iterator I = begin; I != end; ++I) {
359 bool success = false;
360 buf->dispatch(batch_command, &success);
361 final_success |= success;
364 return !final_success;
367 // Force adding of font path _before_ Application is initialized
368 support::addFontResources();
370 // Let the frontend parse and remove all arguments that it knows
371 pimpl_->application_.reset(createApplication(argc, argv));
376 /* Create a CoreApplication class that will provide the main event loop
377 * and the socket callback registering. With Qt4, only QtCore
378 * library would be needed.
379 * When this is done, a server_mode could be created and the following two
380 * line would be moved out from here.
382 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
383 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
384 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
386 // Parse and remove all known arguments in the LyX singleton
387 // Give an error for all remaining ones.
388 int exit_status = init(argc, argv);
390 // Kill the application object before exiting.
391 pimpl_->application_.reset();
397 // Start the real execution loop.
398 exit_status = pimpl_->application_->exec();
402 // Restore original font resources after Application is destroyed.
403 support::restoreFontResources();
409 void LyX::prepareExit()
411 // Set a flag that we do quitting from the program,
412 // so no refreshes are necessary.
415 // close buffers first
416 pimpl_->buffer_list_.closeAll();
418 // do any other cleanup procedures now
419 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
421 // Prevent the deletion of /tmp if LyX was called with invalid
422 // arguments. Does not work on windows.
423 // FIXME: Fix the real bug instead.
424 if (package().temp_dir() == "/tmp") {
425 lyxerr << "Not deleting /tmp." << 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);
437 if (pimpl_->session_)
438 pimpl_->session_->writeFile();
439 pimpl_->session_.reset();
440 pimpl_->lyx_server_.reset();
441 pimpl_->lyx_socket_.reset();
444 // Kill the application object before exiting. This avoids crashes
445 // when exiting on Linux.
446 if (pimpl_->application_)
447 pimpl_->application_.reset();
451 void LyX::earlyExit(int status)
453 BOOST_ASSERT(pimpl_->application_.get());
454 // LyX::pimpl_::application_ is not initialised at this
455 // point so it's safe to just exit after some cleanup.
461 int LyX::init(int & argc, char * argv[])
463 // check for any spurious extra arguments
464 // other than documents
465 for (int argi = 1; argi < argc ; ++argi) {
466 if (argv[argi][0] == '-') {
468 bformat(_("Wrong command line option `%1$s'. Exiting."),
469 from_utf8(argv[argi]))) << endl;
474 // Initialization of LyX (reads lyxrc and more)
475 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
476 bool success = init();
477 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
481 for (int argi = argc - 1; argi >= 1; --argi) {
482 // get absolute path of file and add ".lyx" to
483 // the filename if necessary
484 pimpl_->files_to_load_.push_back(fileSearch(string(), os::internal_path(argv[argi]), "lyx"));
488 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
494 void LyX::loadFiles()
496 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
497 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
499 for (; it != end; ++it) {
503 Buffer * const b = newFile(it->absFilename(), string(), true);
504 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
505 if (loadLyXFile(buf, *it)) {
506 ErrorList const & el = buf->errorList("Parse");
508 for_each(el.begin(), el.end(),
509 boost::bind(&LyX::printError, this, _1));
512 pimpl_->buffer_list_.release(buf);
517 void LyX::execBatchCommands()
519 // The advantage of doing this here is that the event loop
520 // is already started. So any need for interaction will be
524 // Execute batch commands if available
525 if (batch_command.empty())
528 lyxerr[Debug::INIT] << "About to handle -x '"
529 << batch_command << '\'' << endl;
531 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
535 void LyX::restoreGuiSession()
537 LyXView * view = newLyXView();
539 // if some files were specified at command-line we assume that the
540 // user wants to edit *these* files and not to restore the session.
541 if (!pimpl_->files_to_load_.empty()) {
542 for_each(pimpl_->files_to_load_.begin(),
543 pimpl_->files_to_load_.end(),
544 bind(&LyXView::loadLyXFile, view, _1, true));
545 // clear this list to save a few bytes of RAM
546 pimpl_->files_to_load_.clear();
547 pimpl_->session_->lastOpened().clear();
551 if (!lyxrc.load_session)
554 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
555 // do not add to the lastfile list since these files are restored from
556 // last session, and should be already there (regular files), or should
557 // not be added at all (help files).
558 for_each(lastopened.begin(), lastopened.end(),
559 bind(&LyXView::loadLyXFile, view, _1, false));
561 // clear this list to save a few bytes of RAM
562 pimpl_->session_->lastOpened().clear();
566 LyXView * LyX::newLyXView()
571 // determine windows size and position, from lyxrc and/or session
573 unsigned int width = 690;
574 unsigned int height = 510;
575 // default icon size, will be overwritten by stored session value
576 unsigned int iconSizeXY = 0;
577 bool maximize = false;
579 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
580 width = lyxrc.geometry_width;
581 height = lyxrc.geometry_height;
583 // if lyxrc returns (0,0), then use session info
585 string val = session().sessionInfo().load("WindowWidth");
587 width = convert<unsigned int>(val);
588 val = session().sessionInfo().load("WindowHeight");
590 height = convert<unsigned int>(val);
591 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
593 val = session().sessionInfo().load("IconSizeXY");
595 iconSizeXY = convert<unsigned int>(val);
598 // if user wants to restore window position
601 if (lyxrc.geometry_xysaved) {
602 string val = session().sessionInfo().load("WindowPosX");
604 posx = convert<int>(val);
605 val = session().sessionInfo().load("WindowPosY");
607 posy = convert<int>(val);
610 if (!geometryArg.empty())
616 // create the main window
617 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
625 The SIGHUP signal does not exist on Windows and does not need to be handled.
627 Windows handles SIGFPE and SIGSEGV signals as expected.
629 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
630 cause a new thread to be spawned. This may well result in unexpected
631 behaviour by the single-threaded LyX.
633 SIGTERM signals will come only from another process actually sending
634 that signal using 'raise' in Windows' POSIX compatability layer. It will
635 not come from the general "terminate process" methods that everyone
636 actually uses (and which can't be trapped). Killing an app 'politely' on
637 Windows involves first sending a WM_CLOSE message, something that is
638 caught already by the Qt frontend.
640 For more information see:
642 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
643 ...signals are mostly useless on Windows for a variety of reasons that are
646 'UNIX Application Migration Guide, Chapter 9'
647 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
649 'How To Terminate an Application "Cleanly" in Win32'
650 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
654 static void error_handler(int err_sig)
656 // Throw away any signals other than the first one received.
657 static sig_atomic_t handling_error = false;
660 handling_error = true;
662 // We have received a signal indicating a fatal error, so
663 // try and save the data ASAP.
664 LyX::cref().emergencyCleanup();
666 // These lyxerr calls may or may not work:
668 // Signals are asynchronous, so the main program may be in a very
669 // fragile state when a signal is processed and thus while a signal
670 // handler function executes.
671 // In general, therefore, we should avoid performing any
672 // I/O operations or calling most library and system functions from
675 // This shouldn't matter here, however, as we've already invoked
680 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
684 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
687 lyxerr << "\nlyx: SIGSEGV signal caught\n"
688 "Sorry, you have found a bug in LyX. "
689 "Please read the bug-reporting instructions "
690 "in Help->Introduction and send us a bug report, "
691 "if necessary. Thanks !\nBye." << endl;
699 // Deinstall the signal handlers
701 signal(SIGHUP, SIG_DFL);
703 signal(SIGINT, SIG_DFL);
704 signal(SIGFPE, SIG_DFL);
705 signal(SIGSEGV, SIG_DFL);
706 signal(SIGTERM, SIG_DFL);
709 if (err_sig == SIGSEGV ||
710 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
712 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
721 void LyX::printError(ErrorItem const & ei)
723 docstring tmp = _("LyX: ") + ei.error + char_type(':')
725 std::cerr << to_utf8(tmp) << std::endl;
729 void LyX::initGuiFont()
731 if (lyxrc.roman_font_name.empty())
732 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
734 if (lyxrc.sans_font_name.empty())
735 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
737 if (lyxrc.typewriter_font_name.empty())
738 lyxrc.typewriter_font_name
739 = pimpl_->application_->typewriterFontName();
746 signal(SIGHUP, error_handler);
748 signal(SIGFPE, error_handler);
749 signal(SIGSEGV, error_handler);
750 signal(SIGINT, error_handler);
751 signal(SIGTERM, error_handler);
752 // SIGPIPE can be safely ignored.
754 lyxrc.tempdir_path = package().temp_dir();
755 lyxrc.document_path = package().document_dir();
757 if (lyxrc.template_path.empty()) {
758 lyxrc.template_path = addPath(package().system_support(),
763 // Read configuration files
766 // This one may have been distributed along with LyX.
767 if (!readRcFile("lyxrc.dist"))
770 // Set the PATH correctly.
771 #if !defined (USE_POSIX_PACKAGING)
772 // Add the directory containing the LyX executable to the path
773 // so that LyX can find things like tex2lyx.
774 if (package().build_support().empty())
775 prependEnvPath("PATH", package().binary_dir());
777 if (!lyxrc.path_prefix.empty())
778 prependEnvPath("PATH", lyxrc.path_prefix);
780 // Check that user LyX directory is ok.
781 if (queryUserLyXDir(package().explicit_user_support()))
782 reconfigureUserLyXDir();
784 // no need for a splash when there is no GUI
789 // This one is generated in user_support directory by lib/configure.py.
790 if (!readRcFile("lyxrc.defaults"))
793 // Query the OS to know what formats are viewed natively
794 formats.setAutoOpen();
796 // Read lyxrc.dist again to be able to override viewer auto-detection.
797 readRcFile("lyxrc.dist");
799 system_lyxrc = lyxrc;
800 system_formats = formats;
801 system_converters = converters;
802 system_movers = movers;
803 system_lcolor = lcolor;
805 // This one is edited through the preferences dialog.
806 if (!readRcFile("preferences"))
809 if (!readEncodingsFile("encodings"))
811 if (!readLanguagesFile("languages"))
815 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
821 pimpl_->toplevel_keymap_.reset(new kb_keymap);
822 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
823 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
825 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
828 if (!readUIFile(lyxrc.ui_file))
832 if (lyxerr.debugging(Debug::LYXRC))
835 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
836 if (!lyxrc.path_prefix.empty())
837 prependEnvPath("PATH", lyxrc.path_prefix);
839 FileName const document_path(lyxrc.document_path);
840 if (fs::exists(document_path.toFilesystemEncoding()) &&
841 fs::is_directory(document_path.toFilesystemEncoding()))
842 package().document_dir() = lyxrc.document_path;
844 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
845 if (package().temp_dir().empty()) {
846 Alert::error(_("Could not create temporary directory"),
847 bformat(_("Could not create a temporary directory in\n"
848 "%1$s. Make sure that this\n"
849 "path exists and is writable and try again."),
850 from_utf8(lyxrc.tempdir_path)));
851 // createLyXTmpDir() tries sufficiently hard to create a
852 // usable temp dir, so the probability to come here is
853 // close to zero. We therefore don't try to overcome this
854 // problem with e.g. asking the user for a new path and
855 // trying again but simply exit.
859 if (lyxerr.debugging(Debug::INIT)) {
860 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
863 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
864 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
866 // This must happen after package initialization and after lyxrc is
867 // read, therefore it can't be done by a static object.
868 ConverterCache::init();
874 void LyX::defaultKeyBindings(kb_keymap * kbmap)
876 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
877 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
878 kbmap->bind("Up", FuncRequest(LFUN_UP));
879 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
881 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
882 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
883 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
884 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
886 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
887 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
888 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
889 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
891 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
892 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
894 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
895 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
897 // kbmap->bindings to enable the use of the numeric keypad
899 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
900 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
901 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
902 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
903 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
904 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
906 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
915 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
916 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
917 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
918 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
919 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
920 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
921 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
922 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
926 void LyX::emergencyCleanup() const
928 // what to do about tmpfiles is non-obvious. we would
929 // like to delete any we find, but our lyxdir might
930 // contain documents etc. which might be helpful on
933 pimpl_->buffer_list_.emergencyWriteAll();
935 if (pimpl_->lyx_server_)
936 pimpl_->lyx_server_->emergencyCleanup();
937 pimpl_->lyx_server_.reset();
938 pimpl_->lyx_socket_.reset();
943 void LyX::deadKeyBindings(kb_keymap * kbmap)
945 // bindKeyings for transparent handling of deadkeys
946 // The keysyms are gotten from XFree86 X11R6
947 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
948 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
949 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
950 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
951 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
952 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
953 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
954 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
955 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
956 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
957 // nothing with this name
958 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
959 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
960 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
961 // nothing with this name either...
962 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
963 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
964 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
965 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
971 // return true if file does not exist or is older than configure.py.
972 bool needsUpdate(string const & file)
974 // We cannot initialize configure_script directly because the package
975 // is not initialized yet when static objects are constructed.
976 static string configure_script;
977 static bool firstrun = true;
979 configure_script = FileName(addName(
980 package().system_support(),
981 "configure.py")).toFilesystemEncoding();
985 string const absfile = FileName(addName(
986 package().user_support(), file)).toFilesystemEncoding();
987 return (! fs::exists(absfile))
988 || (fs::last_write_time(configure_script)
989 > fs::last_write_time(absfile));
995 bool LyX::queryUserLyXDir(bool explicit_userdir)
997 // Does user directory exist?
998 string const user_support =
999 FileName(package().user_support()).toFilesystemEncoding();
1000 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1001 first_start = false;
1003 return needsUpdate("lyxrc.defaults")
1004 || needsUpdate("textclass.lst")
1005 || needsUpdate("packages.lst");
1008 first_start = !explicit_userdir;
1010 // If the user specified explicitly a directory, ask whether
1011 // to create it. If the user says "no", then exit.
1012 if (explicit_userdir &&
1014 _("Missing user LyX directory"),
1015 bformat(_("You have specified a non-existent user "
1016 "LyX directory, %1$s.\n"
1017 "It is needed to keep your own configuration."),
1018 from_utf8(package().user_support())),
1020 _("&Create directory"),
1022 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1023 earlyExit(EXIT_FAILURE);
1026 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1027 from_utf8(package().user_support())))
1030 if (!createDirectory(package().user_support(), 0755)) {
1031 // Failed, so let's exit.
1032 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1034 earlyExit(EXIT_FAILURE);
1041 bool LyX::readRcFile(string const & name)
1043 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1045 FileName const lyxrc_path = libFileSearch(string(), name);
1046 if (!lyxrc_path.empty()) {
1048 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1050 if (lyxrc.read(lyxrc_path) < 0) {
1051 showFileError(name);
1055 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1061 // Read the ui file `name'
1062 bool LyX::readUIFile(string const & name)
1072 struct keyword_item uitags[ui_last - 1] = {
1073 { "include", ui_include },
1074 { "menuset", ui_menuset },
1075 { "toolbar", ui_toolbar },
1076 { "toolbars", ui_toolbars }
1079 // Ensure that a file is read only once (prevents include loops)
1080 static std::list<string> uifiles;
1081 std::list<string>::const_iterator it = uifiles.begin();
1082 std::list<string>::const_iterator end = uifiles.end();
1083 it = std::find(it, end, name);
1085 lyxerr[Debug::INIT] << "UI file '" << name
1086 << "' has been read already. "
1087 << "Is this an include loop?"
1092 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1094 FileName const ui_path = libFileSearch("ui", name, "ui");
1096 if (ui_path.empty()) {
1097 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1098 showFileError(name);
1101 uifiles.push_back(name);
1103 lyxerr[Debug::INIT] << "Found " << name
1104 << " in " << ui_path << endl;
1105 LyXLex lex(uitags, ui_last - 1);
1106 lex.setFile(ui_path);
1108 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1112 if (lyxerr.debugging(Debug::PARSER))
1113 lex.printTable(lyxerr);
1115 while (lex.isOK()) {
1116 switch (lex.lex()) {
1119 string const file = lex.getString();
1120 if (!readUIFile(file))
1125 menubackend.read(lex);
1129 toolbarbackend.read(lex);
1133 toolbarbackend.readToolbars(lex);
1137 if (!rtrim(lex.getString()).empty())
1138 lex.printError("LyX::ReadUIFile: "
1139 "Unknown menu tag: `$$Token'");
1147 // Read the languages file `name'
1148 bool LyX::readLanguagesFile(string const & name)
1150 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1152 FileName const lang_path = libFileSearch(string(), name);
1153 if (lang_path.empty()) {
1154 showFileError(name);
1157 languages.read(lang_path);
1162 // Read the encodings file `name'
1163 bool LyX::readEncodingsFile(string const & name)
1165 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1167 FileName const enc_path = libFileSearch(string(), name);
1168 if (enc_path.empty()) {
1169 showFileError(name);
1172 encodings.read(enc_path);
1181 /// return the the number of arguments consumed
1182 typedef boost::function<int(string const &, string const &)> cmd_helper;
1184 int parse_dbg(string const & arg, string const &)
1187 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1188 Debug::showTags(lyxerr);
1191 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1193 lyxerr.level(Debug::value(arg));
1194 Debug::showLevel(lyxerr, lyxerr.level());
1199 int parse_help(string const &, string const &)
1202 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1203 "Command line switches (case sensitive):\n"
1204 "\t-help summarize LyX usage\n"
1205 "\t-userdir dir set user directory to dir\n"
1206 "\t-sysdir dir set system directory to dir\n"
1207 "\t-geometry WxH+X+Y set geometry of the main window\n"
1208 "\t-dbg feature[,feature]...\n"
1209 " select the features to debug.\n"
1210 " Type `lyx -dbg' to see the list of features\n"
1211 "\t-x [--execute] command\n"
1212 " where command is a lyx command.\n"
1213 "\t-e [--export] fmt\n"
1214 " where fmt is the export format of choice.\n"
1215 "\t-i [--import] fmt file.xxx\n"
1216 " where fmt is the import format of choice\n"
1217 " and file.xxx is the file to be imported.\n"
1218 "\t-version summarize version and build info\n"
1219 "Check the LyX man page for more details.")) << endl;
1224 int parse_version(string const &, string const &)
1226 lyxerr << "LyX " << lyx_version
1227 << " (" << lyx_release_date << ")" << endl;
1228 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1230 lyxerr << lyx_version_info << endl;
1235 int parse_sysdir(string const & arg, string const &)
1238 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1241 cl_system_support = arg;
1245 int parse_userdir(string const & arg, string const &)
1248 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1251 cl_user_support = arg;
1255 int parse_execute(string const & arg, string const &)
1258 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1265 int parse_export(string const & type, string const &)
1268 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1269 "--export switch")) << endl;
1272 batch = "buffer-export " + type;
1277 int parse_import(string const & type, string const & file)
1280 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1281 "--import switch")) << endl;
1285 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1289 batch = "buffer-import " + type + ' ' + file;
1293 int parse_geometry(string const & arg1, string const &)
1296 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1297 // remove also the arg
1300 // don't remove "-geometry"
1309 void LyX::easyParse(int & argc, char * argv[])
1311 std::map<string, cmd_helper> cmdmap;
1313 cmdmap["-dbg"] = parse_dbg;
1314 cmdmap["-help"] = parse_help;
1315 cmdmap["--help"] = parse_help;
1316 cmdmap["-version"] = parse_version;
1317 cmdmap["--version"] = parse_version;
1318 cmdmap["-sysdir"] = parse_sysdir;
1319 cmdmap["-userdir"] = parse_userdir;
1320 cmdmap["-x"] = parse_execute;
1321 cmdmap["--execute"] = parse_execute;
1322 cmdmap["-e"] = parse_export;
1323 cmdmap["--export"] = parse_export;
1324 cmdmap["-i"] = parse_import;
1325 cmdmap["--import"] = parse_import;
1326 cmdmap["-geometry"] = parse_geometry;
1328 for (int i = 1; i < argc; ++i) {
1329 std::map<string, cmd_helper>::const_iterator it
1330 = cmdmap.find(argv[i]);
1332 // don't complain if not found - may be parsed later
1333 if (it == cmdmap.end())
1336 string arg((i + 1 < argc) ? argv[i + 1] : "");
1337 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1339 int const remove = 1 + it->second(arg, arg2);
1341 // Now, remove used arguments by shifting
1342 // the following ones remove places down.
1345 for (int j = i; j < argc; ++j)
1346 argv[j] = argv[j + remove];
1351 batch_command = batch;
1355 FuncStatus getStatus(FuncRequest const & action)
1357 return LyX::ref().lyxFunc().getStatus(action);
1361 void dispatch(FuncRequest const & action)
1363 LyX::ref().lyxFunc().dispatch(action);
1367 BufferList & theBufferList()
1369 return LyX::ref().bufferList();
1373 LyXFunc & theLyXFunc()
1375 return LyX::ref().lyxFunc();
1379 LyXServer & theLyXServer()
1381 // FIXME: this should not be use_gui dependent
1382 BOOST_ASSERT(use_gui);
1383 return LyX::ref().server();
1387 LyXServerSocket & theLyXServerSocket()
1389 // FIXME: this should not be use_gui dependent
1390 BOOST_ASSERT(use_gui);
1391 return LyX::ref().socket();
1395 kb_keymap & theTopLevelKeymap()
1397 BOOST_ASSERT(use_gui);
1398 return LyX::ref().topLevelKeymap();
1402 IconvProcessor & utf8ToUcs4()
1404 return LyX::ref().iconvProcessor();