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>
71 using support::addName;
72 using support::addPath;
73 using support::bformat;
74 using support::createDirectory;
75 using support::createLyXTmpDir;
76 using support::destroyDir;
77 using support::FileName;
78 using support::fileSearch;
79 using support::getEnv;
80 using support::i18nLibFileSearch;
81 using support::libFileSearch;
82 using support::package;
83 using support::prependEnvPath;
85 using support::Systemcall;
87 namespace Alert = frontend::Alert;
88 namespace os = support::os;
89 namespace fs = boost::filesystem;
96 #ifndef CXX_GLOBAL_CSTD
103 frontend::Application * theApp = 0;
105 /// are we using the GUI at all?
107 * We default to true and this is changed to false when the export feature is used.
114 // Filled with the command line arguments "foo" of "-sysdir foo" or
116 string cl_system_support;
117 string cl_user_support;
119 LyX * singleton_ = 0;
121 void showFileError(string const & error)
123 Alert::warning(_("Could not read configuration file"),
124 bformat(_("Error while reading the configuration file\n%1$s.\n"
125 "Please check your installation."), from_utf8(error)));
129 void reconfigureUserLyXDir()
131 string const configure_command = package().configure_command();
133 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
134 support::Path p(package().user_support());
136 one.startscript(Systemcall::Wait, configure_command);
137 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
143 /// The main application class private implementation.
144 struct LyX::Singletons
146 Singletons(): iconv(ucs4_codeset, "UTF-8")
149 /// our function handler
152 BufferList buffer_list_;
154 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
156 boost::scoped_ptr<LyXServer> lyx_server_;
158 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
160 boost::scoped_ptr<frontend::Application> application_;
161 /// lyx session, containing lastfiles, lastfilepos, and lastopened
162 boost::scoped_ptr<Session> session_;
165 IconvProcessor iconv;
171 // Static data are not treated in the same way at all on the Mac (and
172 // the LyX singleton has static methods). This is the reason why the
173 // exit command on the Mac bypasses our dispatch machinery altogether.
174 // On Linux and Windows we won't pass a second time through quit()
175 // because quitting will already be set to true.
183 BOOST_ASSERT(singleton_);
188 LyX const & LyX::cref()
190 BOOST_ASSERT(singleton_);
196 : first_start(false), geometryOption_(false)
199 pimpl_.reset(new Singletons);
203 BufferList & LyX::bufferList()
205 return pimpl_->buffer_list_;
209 BufferList const & LyX::bufferList() const
211 return pimpl_->buffer_list_;
215 Session & LyX::session()
217 BOOST_ASSERT(pimpl_->session_.get());
218 return *pimpl_->session_.get();
222 Session const & LyX::session() const
224 BOOST_ASSERT(pimpl_->session_.get());
225 return *pimpl_->session_.get();
229 LyXFunc & LyX::lyxFunc()
231 return pimpl_->lyxfunc_;
235 LyXFunc const & LyX::lyxFunc() const
237 return pimpl_->lyxfunc_;
241 LyXServer & LyX::server()
243 BOOST_ASSERT(pimpl_->lyx_server_.get());
244 return *pimpl_->lyx_server_.get();
248 LyXServer const & LyX::server() const
250 BOOST_ASSERT(pimpl_->lyx_server_.get());
251 return *pimpl_->lyx_server_.get();
255 LyXServerSocket & LyX::socket()
257 BOOST_ASSERT(pimpl_->lyx_socket_.get());
258 return *pimpl_->lyx_socket_.get();
262 LyXServerSocket const & LyX::socket() const
264 BOOST_ASSERT(pimpl_->lyx_socket_.get());
265 return *pimpl_->lyx_socket_.get();
269 frontend::Application & LyX::application()
271 BOOST_ASSERT(pimpl_->application_.get());
272 return *pimpl_->application_.get();
276 frontend::Application const & LyX::application() const
278 BOOST_ASSERT(pimpl_->application_.get());
279 return *pimpl_->application_.get();
283 kb_keymap & LyX::topLevelKeymap()
285 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
286 return *pimpl_->toplevel_keymap_.get();
290 IconvProcessor & LyX::iconvProcessor()
292 return pimpl_->iconv;
296 kb_keymap const & LyX::topLevelKeymap() const
298 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
299 return *pimpl_->toplevel_keymap_.get();
303 Buffer const * const LyX::updateInset(InsetBase const * inset) const
308 Buffer const * buffer_ptr = 0;
309 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
310 vector<int>::const_iterator it = view_ids.begin();
311 vector<int>::const_iterator const end = view_ids.end();
312 for (; it != end; ++it) {
314 pimpl_->application_->gui().view(*it).updateInset(inset);
322 int LyX::exec(int & argc, char * argv[])
324 // Here we need to parse the command line. At least
325 // we need to parse for "-dbg" and "-help"
326 easyParse(argc, argv);
328 support::init_package(argv[0], cl_system_support, cl_user_support,
329 support::top_build_dir_is_one_level_up);
331 vector<string> files;
334 // FIXME: create a ConsoleApplication
335 int exit_status = loadFiles(argc, argv, files);
341 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
346 BufferList::iterator begin = pimpl_->buffer_list_.begin();
347 BufferList::iterator end = pimpl_->buffer_list_.end();
349 bool final_success = false;
350 for (BufferList::iterator I = begin; I != end; ++I) {
352 bool success = false;
353 buf->dispatch(batch_command, &success);
354 final_success |= success;
357 return !final_success;
360 // Force adding of font path _before_ Application is initialized
361 support::addFontResources();
363 // Let the frontend parse and remove all arguments that it knows
364 pimpl_->application_.reset(createApplication(argc, argv));
365 // FIXME: this global pointer should probably go.
366 theApp = pimpl_->application_.get();
370 // Parse and remove all known arguments in the LyX singleton
371 // Give an error for all remaining ones.
372 int exit_status = loadFiles(argc, argv, files);
374 // Kill the application object before exiting.
375 pimpl_->application_.reset();
381 restoreGuiSession(files);
382 // Start the real execution loop.
385 /* Create a CoreApplication class that will provide the main event loop
386 * and the socket callback registering. With Qt4, only QtCore
387 * library would be needed.
388 * When this is done, a server_mode could be created and the following two
389 * line would be moved out from here.
391 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
392 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
393 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
395 exit_status = pimpl_->application_->exec();
396 // Kill the application object before exiting. This avoid crash
398 pimpl_->application_.reset();
399 // Restore original font resources after Application is destroyed.
400 support::restoreFontResources();
406 void LyX::prepareExit()
408 // Set a flag that we do quitting from the program,
409 // so no refreshes are necessary.
412 // close buffers first
413 pimpl_->buffer_list_.closeAll();
415 // do any other cleanup procedures now
416 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
418 // Prevent the deletion of /tmp if LyX was called with invalid
419 // arguments. Does not work on windows.
420 // FIXME: Fix the real bug instead.
421 if (package().temp_dir() == "/tmp") {
422 lyxerr << "Not deleting /tmp." << endl;
426 if (!destroyDir(package().temp_dir())) {
427 docstring const msg =
428 bformat(_("Unable to remove the temporary directory %1$s"),
429 from_utf8(package().temp_dir()));
430 Alert::warning(_("Unable to remove temporary directory"), msg);
435 void LyX::earlyExit(int status)
437 BOOST_ASSERT(pimpl_->application_.get());
438 // LyX::pimpl_::application_ is not initialised at this
439 // point so it's safe to just exit after some cleanup.
447 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
452 pimpl_->session_->writeFile();
453 pimpl_->lyx_server_.reset();
454 pimpl_->lyx_socket_.reset();
455 pimpl_->application_->exit(0);
461 int LyX::loadFiles(int & argc, char * argv[],
462 vector<string> & files)
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 // check for any remaining extra arguments other than
484 // document file names. These will be passed out to the
486 if (argv[argi][0] == '-')
488 files.push_back(os::internal_path(argv[argi]));
492 files.push_back(i18nLibFileSearch("examples", "splash.lyx").absFilename());
494 Buffer * last_loaded = 0;
496 vector<string>::const_iterator it = files.begin();
497 vector<string>::const_iterator end = files.end();
499 for (; it != end; ++it) {
500 // get absolute path of file and add ".lyx" to
501 // the filename if necessary
502 string s = fileSearch(string(), *it, "lyx").absFilename();
504 Buffer * const b = newFile(*it, string(), true);
508 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
509 if (loadLyXFile(buf, s)) {
511 ErrorList const & el = buf->errorList("Parse");
513 for_each(el.begin(), el.end(),
514 boost::bind(&LyX::printError, this, _1));
517 pimpl_->buffer_list_.release(buf);
521 files.clear(); // the files are already loaded
527 void LyX::execBatchCommands()
529 // Execute batch commands if available
530 if (batch_command.empty())
533 lyxerr[Debug::INIT] << "About to handle -x '"
534 << batch_command << '\'' << endl;
536 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
540 void LyX::restoreGuiSession(vector<string> const & files)
542 LyXView * view = newLyXView();
545 for_each(files.begin(), files.end(),
546 bind(&LyXView::loadLyXFile, view, _1, true));
548 // if a file is specified, I assume that user wants to edit *that* file
549 if (files.empty() && lyxrc.load_session) {
550 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
551 // do not add to the lastfile list since these files are restored from
552 // last seesion, and should be already there (regular files), or should
553 // not be added at all (help files).
554 for_each(lastopened.begin(), lastopened.end(),
555 bind(&LyXView::loadLyXFile, view, _1, false));
557 // clear this list to save a few bytes of RAM
558 pimpl_->session_->lastOpened().clear();
562 LyXView * LyX::newLyXView()
567 // determine windows size and position, from lyxrc and/or session
569 unsigned int width = 690;
570 unsigned int height = 510;
571 // default icon size, will be overwritten by stored session value
572 unsigned int iconSizeXY = 0;
573 bool maximize = false;
575 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
576 width = lyxrc.geometry_width;
577 height = lyxrc.geometry_height;
579 // if lyxrc returns (0,0), then use session info
581 string val = session().sessionInfo().load("WindowWidth");
583 width = convert<unsigned int>(val);
584 val = session().sessionInfo().load("WindowHeight");
586 height = convert<unsigned int>(val);
587 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
589 val = session().sessionInfo().load("IconSizeXY");
591 iconSizeXY = convert<unsigned int>(val);
594 // if user wants to restore window position
597 if (lyxrc.geometry_xysaved) {
598 string val = session().sessionInfo().load("WindowPosX");
600 posx = convert<int>(val);
601 val = session().sessionInfo().load("WindowPosY");
603 posy = convert<int>(val);
606 if (geometryOption_) {
610 // create the main window
611 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY);
619 The SIGHUP signal does not exist on Windows and does not need to be handled.
621 Windows handles SIGFPE and SIGSEGV signals as expected.
623 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
624 cause a new thread to be spawned. This may well result in unexpected
625 behaviour by the single-threaded LyX.
627 SIGTERM signals will come only from another process actually sending
628 that signal using 'raise' in Windows' POSIX compatability layer. It will
629 not come from the general "terminate process" methods that everyone
630 actually uses (and which can't be trapped). Killing an app 'politely' on
631 Windows involves first sending a WM_CLOSE message, something that is
632 caught already by the Qt frontend.
634 For more information see:
636 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
637 ...signals are mostly useless on Windows for a variety of reasons that are
640 'UNIX Application Migration Guide, Chapter 9'
641 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
643 'How To Terminate an Application "Cleanly" in Win32'
644 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
648 static void error_handler(int err_sig)
650 // Throw away any signals other than the first one received.
651 static sig_atomic_t handling_error = false;
654 handling_error = true;
656 // We have received a signal indicating a fatal error, so
657 // try and save the data ASAP.
658 LyX::cref().emergencyCleanup();
660 // These lyxerr calls may or may not work:
662 // Signals are asynchronous, so the main program may be in a very
663 // fragile state when a signal is processed and thus while a signal
664 // handler function executes.
665 // In general, therefore, we should avoid performing any
666 // I/O operations or calling most library and system functions from
669 // This shouldn't matter here, however, as we've already invoked
674 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
678 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
681 lyxerr << "\nlyx: SIGSEGV signal caught\n"
682 "Sorry, you have found a bug in LyX. "
683 "Please read the bug-reporting instructions "
684 "in Help->Introduction and send us a bug report, "
685 "if necessary. Thanks !\nBye." << endl;
693 // Deinstall the signal handlers
695 signal(SIGHUP, SIG_DFL);
697 signal(SIGINT, SIG_DFL);
698 signal(SIGFPE, SIG_DFL);
699 signal(SIGSEGV, SIG_DFL);
700 signal(SIGTERM, SIG_DFL);
703 if (err_sig == SIGSEGV ||
704 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
706 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
715 void LyX::printError(ErrorItem const & ei)
717 docstring tmp = _("LyX: ") + ei.error + char_type(':')
719 std::cerr << to_utf8(tmp) << std::endl;
723 void LyX::initGuiFont()
725 if (lyxrc.roman_font_name.empty())
726 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
728 if (lyxrc.sans_font_name.empty())
729 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
731 if (lyxrc.typewriter_font_name.empty())
732 lyxrc.typewriter_font_name
733 = pimpl_->application_->typewriterFontName();
740 signal(SIGHUP, error_handler);
742 signal(SIGFPE, error_handler);
743 signal(SIGSEGV, error_handler);
744 signal(SIGINT, error_handler);
745 signal(SIGTERM, error_handler);
746 // SIGPIPE can be safely ignored.
748 lyxrc.tempdir_path = package().temp_dir();
749 lyxrc.document_path = package().document_dir();
751 if (lyxrc.template_path.empty()) {
752 lyxrc.template_path = addPath(package().system_support(),
757 // Read configuration files
760 // This one may have been distributed along with LyX.
761 if (!readRcFile("lyxrc.dist"))
764 // Set the PATH correctly.
765 #if !defined (USE_POSIX_PACKAGING)
766 // Add the directory containing the LyX executable to the path
767 // so that LyX can find things like tex2lyx.
768 if (package().build_support().empty())
769 prependEnvPath("PATH", package().binary_dir());
771 if (!lyxrc.path_prefix.empty())
772 prependEnvPath("PATH", lyxrc.path_prefix);
774 // Check that user LyX directory is ok.
775 if (queryUserLyXDir(package().explicit_user_support()))
776 reconfigureUserLyXDir();
778 // no need for a splash when there is no GUI
783 // This one is generated in user_support directory by lib/configure.py.
784 if (!readRcFile("lyxrc.defaults"))
787 // Query the OS to know what formats are viewed natively
788 formats.setAutoOpen();
790 // Read lyxrc.dist again to be able to override viewer auto-detection.
791 readRcFile("lyxrc.dist");
793 system_lyxrc = lyxrc;
794 system_formats = formats;
795 system_converters = converters;
796 system_movers = movers;
797 system_lcolor = lcolor;
799 // This one is edited through the preferences dialog.
800 if (!readRcFile("preferences"))
803 if (!readEncodingsFile("encodings"))
805 if (!readLanguagesFile("languages"))
809 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
815 pimpl_->toplevel_keymap_.reset(new kb_keymap);
816 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
817 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
819 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
822 if (!readUIFile(lyxrc.ui_file))
826 if (lyxerr.debugging(Debug::LYXRC))
829 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
830 if (!lyxrc.path_prefix.empty())
831 prependEnvPath("PATH", lyxrc.path_prefix);
833 if (fs::exists(lyxrc.document_path) &&
834 fs::is_directory(lyxrc.document_path))
835 package().document_dir() = lyxrc.document_path;
837 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
838 if (package().temp_dir().empty()) {
839 Alert::error(_("Could not create temporary directory"),
840 bformat(_("Could not create a temporary directory in\n"
841 "%1$s. Make sure that this\n"
842 "path exists and is writable and try again."),
843 from_utf8(lyxrc.tempdir_path)));
844 // createLyXTmpDir() tries sufficiently hard to create a
845 // usable temp dir, so the probability to come here is
846 // close to zero. We therefore don't try to overcome this
847 // problem with e.g. asking the user for a new path and
848 // trying again but simply exit.
852 if (lyxerr.debugging(Debug::INIT)) {
853 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
856 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
857 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
859 // This must happen after package initialization and after lyxrc is
860 // read, therefore it can't be done by a static object.
861 ConverterCache::init();
867 void LyX::defaultKeyBindings(kb_keymap * kbmap)
869 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
870 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
871 kbmap->bind("Up", FuncRequest(LFUN_UP));
872 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
874 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
875 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
876 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
877 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
879 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
880 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
881 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
882 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
884 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
885 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
887 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
888 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
890 // kbmap->bindings to enable the use of the numeric keypad
892 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
893 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
894 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
895 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
896 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
897 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
898 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
899 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
900 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
901 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
902 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
903 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
904 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
906 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
908 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
909 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
910 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
911 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
912 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
913 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
914 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
915 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
919 void LyX::emergencyCleanup() const
921 // what to do about tmpfiles is non-obvious. we would
922 // like to delete any we find, but our lyxdir might
923 // contain documents etc. which might be helpful on
926 pimpl_->buffer_list_.emergencyWriteAll();
928 pimpl_->lyx_server_->emergencyCleanup();
929 pimpl_->lyx_server_.reset();
930 pimpl_->lyx_socket_.reset();
935 void LyX::deadKeyBindings(kb_keymap * kbmap)
937 // bindKeyings for transparent handling of deadkeys
938 // The keysyms are gotten from XFree86 X11R6
939 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
940 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
941 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
942 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
943 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
944 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
945 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
946 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
947 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
948 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
949 // nothing with this name
950 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
951 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
952 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
953 // nothing with this name either...
954 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
955 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
956 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
957 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
963 // return true if file does not exist or is older than configure.py.
964 bool needsUpdate(string const & file)
966 static string const configure_script =
967 addName(package().system_support(), "configure.py");
968 string const absfile =
969 addName(package().user_support(), file);
971 return (! fs::exists(absfile))
972 || (fs::last_write_time(configure_script)
973 > fs::last_write_time(absfile));
979 bool LyX::queryUserLyXDir(bool explicit_userdir)
981 // Does user directory exist?
982 if (fs::exists(package().user_support()) &&
983 fs::is_directory(package().user_support())) {
986 return needsUpdate("lyxrc.defaults")
987 || needsUpdate("textclass.lst")
988 || needsUpdate("packages.lst");
991 first_start = !explicit_userdir;
993 // If the user specified explicitly a directory, ask whether
994 // to create it. If the user says "no", then exit.
995 if (explicit_userdir &&
997 _("Missing user LyX directory"),
998 bformat(_("You have specified a non-existent user "
999 "LyX directory, %1$s.\n"
1000 "It is needed to keep your own configuration."),
1001 from_utf8(package().user_support())),
1003 _("&Create directory"),
1005 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1006 earlyExit(EXIT_FAILURE);
1009 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1010 from_utf8(package().user_support())))
1013 if (!createDirectory(package().user_support(), 0755)) {
1014 // Failed, so let's exit.
1015 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1017 earlyExit(EXIT_FAILURE);
1024 bool LyX::readRcFile(string const & name)
1026 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1028 FileName const lyxrc_path = libFileSearch(string(), name);
1029 if (!lyxrc_path.empty()) {
1031 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1033 if (lyxrc.read(lyxrc_path) < 0) {
1034 showFileError(name);
1038 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1044 // Read the ui file `name'
1045 bool LyX::readUIFile(string const & name)
1055 struct keyword_item uitags[ui_last - 1] = {
1056 { "include", ui_include },
1057 { "menuset", ui_menuset },
1058 { "toolbar", ui_toolbar },
1059 { "toolbars", ui_toolbars }
1062 // Ensure that a file is read only once (prevents include loops)
1063 static std::list<string> uifiles;
1064 std::list<string>::const_iterator it = uifiles.begin();
1065 std::list<string>::const_iterator end = uifiles.end();
1066 it = std::find(it, end, name);
1068 lyxerr[Debug::INIT] << "UI file '" << name
1069 << "' has been read already. "
1070 << "Is this an include loop?"
1075 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1077 FileName const ui_path = libFileSearch("ui", name, "ui");
1079 if (ui_path.empty()) {
1080 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1081 showFileError(name);
1084 uifiles.push_back(name);
1086 lyxerr[Debug::INIT] << "Found " << name
1087 << " in " << ui_path << endl;
1088 LyXLex lex(uitags, ui_last - 1);
1089 lex.setFile(ui_path);
1091 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1095 if (lyxerr.debugging(Debug::PARSER))
1096 lex.printTable(lyxerr);
1098 while (lex.isOK()) {
1099 switch (lex.lex()) {
1102 string const file = lex.getString();
1103 if (!readUIFile(file))
1108 menubackend.read(lex);
1112 toolbarbackend.read(lex);
1116 toolbarbackend.readToolbars(lex);
1120 if (!rtrim(lex.getString()).empty())
1121 lex.printError("LyX::ReadUIFile: "
1122 "Unknown menu tag: `$$Token'");
1130 // Read the languages file `name'
1131 bool LyX::readLanguagesFile(string const & name)
1133 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1135 FileName const lang_path = libFileSearch(string(), name);
1136 if (lang_path.empty()) {
1137 showFileError(name);
1140 languages.read(lang_path);
1145 // Read the encodings file `name'
1146 bool LyX::readEncodingsFile(string const & name)
1148 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1150 FileName const enc_path = libFileSearch(string(), name);
1151 if (enc_path.empty()) {
1152 showFileError(name);
1155 encodings.read(enc_path);
1164 /// return the the number of arguments consumed
1165 typedef boost::function<int(string const &, string const &)> cmd_helper;
1167 int parse_dbg(string const & arg, string const &)
1170 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1171 Debug::showTags(lyxerr);
1174 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1176 lyxerr.level(Debug::value(arg));
1177 Debug::showLevel(lyxerr, lyxerr.level());
1182 int parse_help(string const &, string const &)
1185 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1186 "Command line switches (case sensitive):\n"
1187 "\t-help summarize LyX usage\n"
1188 "\t-userdir dir set user directory to dir\n"
1189 "\t-sysdir dir set system directory to dir\n"
1190 "\t-geometry WxH+X+Y set geometry of the main window\n"
1191 "\t-dbg feature[,feature]...\n"
1192 " select the features to debug.\n"
1193 " Type `lyx -dbg' to see the list of features\n"
1194 "\t-x [--execute] command\n"
1195 " where command is a lyx command.\n"
1196 "\t-e [--export] fmt\n"
1197 " where fmt is the export format of choice.\n"
1198 "\t-i [--import] fmt file.xxx\n"
1199 " where fmt is the import format of choice\n"
1200 " and file.xxx is the file to be imported.\n"
1201 "\t-version summarize version and build info\n"
1202 "Check the LyX man page for more details.")) << endl;
1207 int parse_version(string const &, string const &)
1209 lyxerr << "LyX " << lyx_version
1210 << " (" << lyx_release_date << ")" << endl;
1211 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1213 lyxerr << lyx_version_info << endl;
1218 int parse_sysdir(string const & arg, string const &)
1221 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1224 cl_system_support = arg;
1228 int parse_userdir(string const & arg, string const &)
1231 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1234 cl_user_support = arg;
1238 int parse_execute(string const & arg, string const &)
1241 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1248 int parse_export(string const & type, string const &)
1251 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1252 "--export switch")) << endl;
1255 batch = "buffer-export " + type;
1260 int parse_import(string const & type, string const & file)
1263 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1264 "--import switch")) << endl;
1268 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1272 batch = "buffer-import " + type + ' ' + file;
1279 void LyX::easyParse(int & argc, char * argv[])
1281 std::map<string, cmd_helper> cmdmap;
1283 cmdmap["-dbg"] = parse_dbg;
1284 cmdmap["-help"] = parse_help;
1285 cmdmap["--help"] = parse_help;
1286 cmdmap["-version"] = parse_version;
1287 cmdmap["--version"] = parse_version;
1288 cmdmap["-sysdir"] = parse_sysdir;
1289 cmdmap["-userdir"] = parse_userdir;
1290 cmdmap["-x"] = parse_execute;
1291 cmdmap["--execute"] = parse_execute;
1292 cmdmap["-e"] = parse_export;
1293 cmdmap["--export"] = parse_export;
1294 cmdmap["-i"] = parse_import;
1295 cmdmap["--import"] = parse_import;
1297 for (int i = 1; i < argc; ++i) {
1298 std::map<string, cmd_helper>::const_iterator it
1299 = cmdmap.find(argv[i]);
1301 // check for X11 -geometry option
1302 if (support::compare(argv[i], "-geometry") == 0)
1303 geometryOption_ = true;
1305 // don't complain if not found - may be parsed later
1306 if (it == cmdmap.end())
1309 string arg((i + 1 < argc) ? argv[i + 1] : "");
1310 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1312 int const remove = 1 + it->second(arg, arg2);
1314 // Now, remove used arguments by shifting
1315 // the following ones remove places down.
1317 for (int j = i; j < argc; ++j)
1318 argv[j] = argv[j + remove];
1322 batch_command = batch;
1326 FuncStatus getStatus(FuncRequest const & action)
1328 return LyX::ref().lyxFunc().getStatus(action);
1332 void dispatch(FuncRequest const & action)
1334 LyX::ref().lyxFunc().dispatch(action);
1338 BufferList & theBufferList()
1340 return LyX::ref().bufferList();
1344 LyXFunc & theLyXFunc()
1346 return LyX::ref().lyxFunc();
1350 LyXServer & theLyXServer()
1352 // FIXME: this should not be use_gui dependent
1353 BOOST_ASSERT(use_gui);
1354 return LyX::ref().server();
1358 LyXServerSocket & theLyXServerSocket()
1360 // FIXME: this should not be use_gui dependent
1361 BOOST_ASSERT(use_gui);
1362 return LyX::ref().socket();
1366 kb_keymap & theTopLevelKeymap()
1368 BOOST_ASSERT(use_gui);
1369 return LyX::ref().topLevelKeymap();
1373 IconvProcessor & utf8ToUcs4()
1375 return LyX::ref().iconvProcessor();