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/filename.h"
53 #include "support/filetools.h"
54 #include "support/fontutils.h"
55 #include "support/lyxlib.h"
56 #include "support/convert.h"
57 #include "support/os.h"
58 #include "support/package.h"
59 #include "support/path.h"
60 #include "support/systemcall.h"
61 #include "support/unicode.h"
63 #include <boost/bind.hpp>
64 #include <boost/filesystem/operations.hpp>
73 using support::addName;
74 using support::addPath;
75 using support::bformat;
76 using support::createDirectory;
77 using support::createLyXTmpDir;
78 using support::destroyDir;
79 using support::FileName;
80 using support::fileSearch;
81 using support::getEnv;
82 using support::i18nLibFileSearch;
83 using support::libFileSearch;
84 using support::package;
85 using support::prependEnvPath;
87 using support::Systemcall;
89 namespace Alert = frontend::Alert;
90 namespace os = support::os;
91 namespace fs = boost::filesystem;
98 #ifndef CXX_GLOBAL_CSTD
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 std::string geometryArg;
121 LyX * singleton_ = 0;
123 void showFileError(string const & error)
125 Alert::warning(_("Could not read configuration file"),
126 bformat(_("Error while reading the configuration file\n%1$s.\n"
127 "Please check your installation."), from_utf8(error)));
131 void reconfigureUserLyXDir()
133 string const configure_command = package().configure_command();
135 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
136 support::Path p(package().user_support());
138 one.startscript(Systemcall::Wait, configure_command);
139 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
145 /// The main application class private implementation.
146 struct LyX::Singletons
148 Singletons(): iconv(ucs4_codeset, "UTF-8")
151 /// our function handler
154 BufferList buffer_list_;
156 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
158 boost::scoped_ptr<LyXServer> lyx_server_;
160 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
162 boost::scoped_ptr<frontend::Application> application_;
163 /// lyx session, containing lastfiles, lastfilepos, and lastopened
164 boost::scoped_ptr<Session> session_;
167 IconvProcessor iconv;
169 /// Files to load at start.
170 vector<FileName> files_to_load_;
174 frontend::Application * theApp()
177 return &singleton_->application();
190 BOOST_ASSERT(singleton_);
195 LyX const & LyX::cref()
197 BOOST_ASSERT(singleton_);
206 pimpl_.reset(new Singletons);
211 BufferList & LyX::bufferList()
213 return pimpl_->buffer_list_;
217 BufferList const & LyX::bufferList() const
219 return pimpl_->buffer_list_;
223 Session & LyX::session()
225 BOOST_ASSERT(pimpl_->session_.get());
226 return *pimpl_->session_.get();
230 Session const & LyX::session() const
232 BOOST_ASSERT(pimpl_->session_.get());
233 return *pimpl_->session_.get();
237 LyXFunc & LyX::lyxFunc()
239 return pimpl_->lyxfunc_;
243 LyXFunc const & LyX::lyxFunc() const
245 return pimpl_->lyxfunc_;
249 LyXServer & LyX::server()
251 BOOST_ASSERT(pimpl_->lyx_server_.get());
252 return *pimpl_->lyx_server_.get();
256 LyXServer const & LyX::server() const
258 BOOST_ASSERT(pimpl_->lyx_server_.get());
259 return *pimpl_->lyx_server_.get();
263 LyXServerSocket & LyX::socket()
265 BOOST_ASSERT(pimpl_->lyx_socket_.get());
266 return *pimpl_->lyx_socket_.get();
270 LyXServerSocket const & LyX::socket() const
272 BOOST_ASSERT(pimpl_->lyx_socket_.get());
273 return *pimpl_->lyx_socket_.get();
277 frontend::Application & LyX::application()
279 BOOST_ASSERT(pimpl_->application_.get());
280 return *pimpl_->application_.get();
284 frontend::Application const & LyX::application() const
286 BOOST_ASSERT(pimpl_->application_.get());
287 return *pimpl_->application_.get();
291 kb_keymap & LyX::topLevelKeymap()
293 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
294 return *pimpl_->toplevel_keymap_.get();
298 IconvProcessor & LyX::iconvProcessor()
300 return pimpl_->iconv;
304 kb_keymap const & LyX::topLevelKeymap() const
306 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
307 return *pimpl_->toplevel_keymap_.get();
311 Buffer const * const LyX::updateInset(InsetBase const * inset) const
316 Buffer const * buffer_ptr = 0;
317 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
318 vector<int>::const_iterator it = view_ids.begin();
319 vector<int>::const_iterator const end = view_ids.end();
320 for (; it != end; ++it) {
322 pimpl_->application_->gui().view(*it).updateInset(inset);
330 int LyX::exec(int & argc, char * argv[])
332 // Here we need to parse the command line. At least
333 // we need to parse for "-dbg" and "-help"
334 easyParse(argc, argv);
336 support::init_package(argv[0], cl_system_support, cl_user_support,
337 support::top_build_dir_is_one_level_up);
340 // FIXME: create a ConsoleApplication
341 int exit_status = init(argc, argv);
349 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
354 BufferList::iterator begin = pimpl_->buffer_list_.begin();
355 BufferList::iterator end = pimpl_->buffer_list_.end();
357 bool final_success = false;
358 for (BufferList::iterator I = begin; I != end; ++I) {
360 bool success = false;
361 buf->dispatch(batch_command, &success);
362 final_success |= success;
365 return !final_success;
368 // Force adding of font path _before_ Application is initialized
369 support::addFontResources();
371 // Let the frontend parse and remove all arguments that it knows
372 pimpl_->application_.reset(createApplication(argc, argv));
377 /* Create a CoreApplication class that will provide the main event loop
378 * and the socket callback registering. With Qt4, only QtCore
379 * library would be needed.
380 * When this is done, a server_mode could be created and the following two
381 * line would be moved out from here.
383 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
384 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
385 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
387 // Parse and remove all known arguments in the LyX singleton
388 // Give an error for all remaining ones.
389 int exit_status = init(argc, argv);
391 // Kill the application object before exiting.
392 pimpl_->application_.reset();
398 // Start the real execution loop.
399 exit_status = pimpl_->application_->exec();
403 // Restore original font resources after Application is destroyed.
404 support::restoreFontResources();
410 void LyX::prepareExit()
412 // Set a flag that we do quitting from the program,
413 // so no refreshes are necessary.
416 // close buffers first
417 pimpl_->buffer_list_.closeAll();
419 // do any other cleanup procedures now
420 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
422 // Prevent the deletion of /tmp if LyX was called with invalid
423 // arguments. Does not work on windows.
424 // FIXME: Fix the real bug instead.
425 if (package().temp_dir() == "/tmp") {
426 lyxerr << "Not deleting /tmp." << endl;
430 if (!destroyDir(package().temp_dir())) {
431 docstring const msg =
432 bformat(_("Unable to remove the temporary directory %1$s"),
433 from_utf8(package().temp_dir()));
434 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 // 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 // get absolute path of file and add ".lyx" to
489 // the filename if necessary
490 pimpl_->files_to_load_.push_back(fileSearch(string(), os::internal_path(argv[argi]), "lyx"));
494 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
500 void LyX::loadFiles()
502 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
503 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
505 for (; it != end; ++it) {
509 Buffer * const b = newFile(it->absFilename(), string(), true);
510 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
511 if (loadLyXFile(buf, *it)) {
512 ErrorList const & el = buf->errorList("Parse");
514 for_each(el.begin(), el.end(),
515 boost::bind(&LyX::printError, this, _1));
518 pimpl_->buffer_list_.release(buf);
523 void LyX::execBatchCommands()
525 // The advantage of doing this here is that the event loop
526 // is already started. So any need for interaction will be
530 // Execute batch commands if available
531 if (batch_command.empty())
534 lyxerr[Debug::INIT] << "About to handle -x '"
535 << batch_command << '\'' << endl;
537 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
541 void LyX::restoreGuiSession()
543 LyXView * view = newLyXView();
545 // if some files were specified at command-line we assume that the
546 // user wants to edit *these* files and not to restore the session.
547 if (!pimpl_->files_to_load_.empty()) {
548 for_each(pimpl_->files_to_load_.begin(),
549 pimpl_->files_to_load_.end(),
550 bind(&LyXView::loadLyXFile, view, _1, true));
551 // clear this list to save a few bytes of RAM
552 pimpl_->files_to_load_.clear();
553 pimpl_->session_->lastOpened().clear();
557 if (!lyxrc.load_session)
560 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
561 // do not add to the lastfile list since these files are restored from
562 // last session, and should be already there (regular files), or should
563 // not be added at all (help files).
564 for_each(lastopened.begin(), lastopened.end(),
565 bind(&LyXView::loadLyXFile, view, _1, false));
567 // clear this list to save a few bytes of RAM
568 pimpl_->session_->lastOpened().clear();
572 LyXView * LyX::newLyXView()
577 // determine windows size and position, from lyxrc and/or session
579 unsigned int width = 690;
580 unsigned int height = 510;
581 // default icon size, will be overwritten by stored session value
582 unsigned int iconSizeXY = 0;
583 bool maximize = false;
585 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
586 width = lyxrc.geometry_width;
587 height = lyxrc.geometry_height;
589 // if lyxrc returns (0,0), then use session info
591 string val = session().sessionInfo().load("WindowWidth");
593 width = convert<unsigned int>(val);
594 val = session().sessionInfo().load("WindowHeight");
596 height = convert<unsigned int>(val);
597 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
599 val = session().sessionInfo().load("IconSizeXY");
601 iconSizeXY = convert<unsigned int>(val);
604 // if user wants to restore window position
607 if (lyxrc.geometry_xysaved) {
608 string val = session().sessionInfo().load("WindowPosX");
610 posx = convert<int>(val);
611 val = session().sessionInfo().load("WindowPosY");
613 posy = convert<int>(val);
616 if (!geometryArg.empty())
622 // create the main window
623 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
631 The SIGHUP signal does not exist on Windows and does not need to be handled.
633 Windows handles SIGFPE and SIGSEGV signals as expected.
635 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
636 cause a new thread to be spawned. This may well result in unexpected
637 behaviour by the single-threaded LyX.
639 SIGTERM signals will come only from another process actually sending
640 that signal using 'raise' in Windows' POSIX compatability layer. It will
641 not come from the general "terminate process" methods that everyone
642 actually uses (and which can't be trapped). Killing an app 'politely' on
643 Windows involves first sending a WM_CLOSE message, something that is
644 caught already by the Qt frontend.
646 For more information see:
648 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
649 ...signals are mostly useless on Windows for a variety of reasons that are
652 'UNIX Application Migration Guide, Chapter 9'
653 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
655 'How To Terminate an Application "Cleanly" in Win32'
656 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
660 static void error_handler(int err_sig)
662 // Throw away any signals other than the first one received.
663 static sig_atomic_t handling_error = false;
666 handling_error = true;
668 // We have received a signal indicating a fatal error, so
669 // try and save the data ASAP.
670 LyX::cref().emergencyCleanup();
672 // These lyxerr calls may or may not work:
674 // Signals are asynchronous, so the main program may be in a very
675 // fragile state when a signal is processed and thus while a signal
676 // handler function executes.
677 // In general, therefore, we should avoid performing any
678 // I/O operations or calling most library and system functions from
681 // This shouldn't matter here, however, as we've already invoked
686 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
690 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
693 lyxerr << "\nlyx: SIGSEGV signal caught\n"
694 "Sorry, you have found a bug in LyX. "
695 "Please read the bug-reporting instructions "
696 "in Help->Introduction and send us a bug report, "
697 "if necessary. Thanks !\nBye." << endl;
705 // Deinstall the signal handlers
707 signal(SIGHUP, SIG_DFL);
709 signal(SIGINT, SIG_DFL);
710 signal(SIGFPE, SIG_DFL);
711 signal(SIGSEGV, SIG_DFL);
712 signal(SIGTERM, SIG_DFL);
715 if (err_sig == SIGSEGV ||
716 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
718 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
727 void LyX::printError(ErrorItem const & ei)
729 docstring tmp = _("LyX: ") + ei.error + char_type(':')
731 std::cerr << to_utf8(tmp) << std::endl;
735 void LyX::initGuiFont()
737 if (lyxrc.roman_font_name.empty())
738 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
740 if (lyxrc.sans_font_name.empty())
741 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
743 if (lyxrc.typewriter_font_name.empty())
744 lyxrc.typewriter_font_name
745 = pimpl_->application_->typewriterFontName();
752 signal(SIGHUP, error_handler);
754 signal(SIGFPE, error_handler);
755 signal(SIGSEGV, error_handler);
756 signal(SIGINT, error_handler);
757 signal(SIGTERM, error_handler);
758 // SIGPIPE can be safely ignored.
760 lyxrc.tempdir_path = package().temp_dir();
761 lyxrc.document_path = package().document_dir();
763 if (lyxrc.template_path.empty()) {
764 lyxrc.template_path = addPath(package().system_support(),
769 // Read configuration files
772 // This one may have been distributed along with LyX.
773 if (!readRcFile("lyxrc.dist"))
776 // Set the PATH correctly.
777 #if !defined (USE_POSIX_PACKAGING)
778 // Add the directory containing the LyX executable to the path
779 // so that LyX can find things like tex2lyx.
780 if (package().build_support().empty())
781 prependEnvPath("PATH", package().binary_dir());
783 if (!lyxrc.path_prefix.empty())
784 prependEnvPath("PATH", lyxrc.path_prefix);
786 // Check that user LyX directory is ok.
787 if (queryUserLyXDir(package().explicit_user_support()))
788 reconfigureUserLyXDir();
790 // no need for a splash when there is no GUI
795 // This one is generated in user_support directory by lib/configure.py.
796 if (!readRcFile("lyxrc.defaults"))
799 // Query the OS to know what formats are viewed natively
800 formats.setAutoOpen();
802 // Read lyxrc.dist again to be able to override viewer auto-detection.
803 readRcFile("lyxrc.dist");
805 system_lyxrc = lyxrc;
806 system_formats = formats;
807 system_converters = converters;
808 system_movers = movers;
809 system_lcolor = lcolor;
811 // This one is edited through the preferences dialog.
812 if (!readRcFile("preferences"))
815 if (!readEncodingsFile("encodings"))
817 if (!readLanguagesFile("languages"))
821 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
827 pimpl_->toplevel_keymap_.reset(new kb_keymap);
828 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
829 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
831 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
834 if (!readUIFile(lyxrc.ui_file))
838 if (lyxerr.debugging(Debug::LYXRC))
841 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
842 if (!lyxrc.path_prefix.empty())
843 prependEnvPath("PATH", lyxrc.path_prefix);
845 if (fs::exists(lyxrc.document_path) &&
846 fs::is_directory(lyxrc.document_path))
847 package().document_dir() = lyxrc.document_path;
849 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
850 if (package().temp_dir().empty()) {
851 Alert::error(_("Could not create temporary directory"),
852 bformat(_("Could not create a temporary directory in\n"
853 "%1$s. Make sure that this\n"
854 "path exists and is writable and try again."),
855 from_utf8(lyxrc.tempdir_path)));
856 // createLyXTmpDir() tries sufficiently hard to create a
857 // usable temp dir, so the probability to come here is
858 // close to zero. We therefore don't try to overcome this
859 // problem with e.g. asking the user for a new path and
860 // trying again but simply exit.
864 if (lyxerr.debugging(Debug::INIT)) {
865 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
868 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
869 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
871 // This must happen after package initialization and after lyxrc is
872 // read, therefore it can't be done by a static object.
873 ConverterCache::init();
879 void LyX::defaultKeyBindings(kb_keymap * kbmap)
881 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
882 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
883 kbmap->bind("Up", FuncRequest(LFUN_UP));
884 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
886 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
887 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
888 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
889 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
891 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
892 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
893 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
894 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
896 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
897 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
899 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
900 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
902 // kbmap->bindings to enable the use of the numeric keypad
904 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
906 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
907 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
915 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
916 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
917 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
918 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
919 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
920 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
921 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
922 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
923 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
924 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
925 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
926 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
927 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
931 void LyX::emergencyCleanup() const
933 // what to do about tmpfiles is non-obvious. we would
934 // like to delete any we find, but our lyxdir might
935 // contain documents etc. which might be helpful on
938 pimpl_->buffer_list_.emergencyWriteAll();
940 if (pimpl_->lyx_server_)
941 pimpl_->lyx_server_->emergencyCleanup();
942 pimpl_->lyx_server_.reset();
943 pimpl_->lyx_socket_.reset();
948 void LyX::deadKeyBindings(kb_keymap * kbmap)
950 // bindKeyings for transparent handling of deadkeys
951 // The keysyms are gotten from XFree86 X11R6
952 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
953 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
954 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
955 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
956 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
957 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
958 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
959 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
960 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
961 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
962 // nothing with this name
963 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
964 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
965 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
966 // nothing with this name either...
967 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
968 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
969 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
970 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
976 // return true if file does not exist or is older than configure.py.
977 bool needsUpdate(string const & file)
979 static string const configure_script =
980 addName(package().system_support(), "configure.py");
981 string const absfile =
982 addName(package().user_support(), file);
984 return (! fs::exists(absfile))
985 || (fs::last_write_time(configure_script)
986 > fs::last_write_time(absfile));
992 bool LyX::queryUserLyXDir(bool explicit_userdir)
994 // Does user directory exist?
995 if (fs::exists(package().user_support()) &&
996 fs::is_directory(package().user_support())) {
999 return needsUpdate("lyxrc.defaults")
1000 || needsUpdate("textclass.lst")
1001 || needsUpdate("packages.lst");
1004 first_start = !explicit_userdir;
1006 // If the user specified explicitly a directory, ask whether
1007 // to create it. If the user says "no", then exit.
1008 if (explicit_userdir &&
1010 _("Missing user LyX directory"),
1011 bformat(_("You have specified a non-existent user "
1012 "LyX directory, %1$s.\n"
1013 "It is needed to keep your own configuration."),
1014 from_utf8(package().user_support())),
1016 _("&Create directory"),
1018 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1019 earlyExit(EXIT_FAILURE);
1022 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1023 from_utf8(package().user_support())))
1026 if (!createDirectory(package().user_support(), 0755)) {
1027 // Failed, so let's exit.
1028 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1030 earlyExit(EXIT_FAILURE);
1037 bool LyX::readRcFile(string const & name)
1039 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1041 FileName const lyxrc_path = libFileSearch(string(), name);
1042 if (!lyxrc_path.empty()) {
1044 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1046 if (lyxrc.read(lyxrc_path) < 0) {
1047 showFileError(name);
1051 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1057 // Read the ui file `name'
1058 bool LyX::readUIFile(string const & name)
1068 struct keyword_item uitags[ui_last - 1] = {
1069 { "include", ui_include },
1070 { "menuset", ui_menuset },
1071 { "toolbar", ui_toolbar },
1072 { "toolbars", ui_toolbars }
1075 // Ensure that a file is read only once (prevents include loops)
1076 static std::list<string> uifiles;
1077 std::list<string>::const_iterator it = uifiles.begin();
1078 std::list<string>::const_iterator end = uifiles.end();
1079 it = std::find(it, end, name);
1081 lyxerr[Debug::INIT] << "UI file '" << name
1082 << "' has been read already. "
1083 << "Is this an include loop?"
1088 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1090 FileName const ui_path = libFileSearch("ui", name, "ui");
1092 if (ui_path.empty()) {
1093 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1094 showFileError(name);
1097 uifiles.push_back(name);
1099 lyxerr[Debug::INIT] << "Found " << name
1100 << " in " << ui_path << endl;
1101 LyXLex lex(uitags, ui_last - 1);
1102 lex.setFile(ui_path);
1104 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1108 if (lyxerr.debugging(Debug::PARSER))
1109 lex.printTable(lyxerr);
1111 while (lex.isOK()) {
1112 switch (lex.lex()) {
1115 string const file = lex.getString();
1116 if (!readUIFile(file))
1121 menubackend.read(lex);
1125 toolbarbackend.read(lex);
1129 toolbarbackend.readToolbars(lex);
1133 if (!rtrim(lex.getString()).empty())
1134 lex.printError("LyX::ReadUIFile: "
1135 "Unknown menu tag: `$$Token'");
1143 // Read the languages file `name'
1144 bool LyX::readLanguagesFile(string const & name)
1146 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1148 FileName const lang_path = libFileSearch(string(), name);
1149 if (lang_path.empty()) {
1150 showFileError(name);
1153 languages.read(lang_path);
1158 // Read the encodings file `name'
1159 bool LyX::readEncodingsFile(string const & name)
1161 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1163 FileName const enc_path = libFileSearch(string(), name);
1164 if (enc_path.empty()) {
1165 showFileError(name);
1168 encodings.read(enc_path);
1177 /// return the the number of arguments consumed
1178 typedef boost::function<int(string const &, string const &)> cmd_helper;
1180 int parse_dbg(string const & arg, string const &)
1183 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1184 Debug::showTags(lyxerr);
1187 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1189 lyxerr.level(Debug::value(arg));
1190 Debug::showLevel(lyxerr, lyxerr.level());
1195 int parse_help(string const &, string const &)
1198 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1199 "Command line switches (case sensitive):\n"
1200 "\t-help summarize LyX usage\n"
1201 "\t-userdir dir set user directory to dir\n"
1202 "\t-sysdir dir set system directory to dir\n"
1203 "\t-geometry WxH+X+Y set geometry of the main window\n"
1204 "\t-dbg feature[,feature]...\n"
1205 " select the features to debug.\n"
1206 " Type `lyx -dbg' to see the list of features\n"
1207 "\t-x [--execute] command\n"
1208 " where command is a lyx command.\n"
1209 "\t-e [--export] fmt\n"
1210 " where fmt is the export format of choice.\n"
1211 "\t-i [--import] fmt file.xxx\n"
1212 " where fmt is the import format of choice\n"
1213 " and file.xxx is the file to be imported.\n"
1214 "\t-version summarize version and build info\n"
1215 "Check the LyX man page for more details.")) << endl;
1220 int parse_version(string const &, string const &)
1222 lyxerr << "LyX " << lyx_version
1223 << " (" << lyx_release_date << ")" << endl;
1224 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1226 lyxerr << lyx_version_info << endl;
1231 int parse_sysdir(string const & arg, string const &)
1234 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1237 cl_system_support = arg;
1241 int parse_userdir(string const & arg, string const &)
1244 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1247 cl_user_support = arg;
1251 int parse_execute(string const & arg, string const &)
1254 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1261 int parse_export(string const & type, string const &)
1264 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1265 "--export switch")) << endl;
1268 batch = "buffer-export " + type;
1273 int parse_import(string const & type, string const & file)
1276 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1277 "--import switch")) << endl;
1281 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1285 batch = "buffer-import " + type + ' ' + file;
1289 int parse_geometry(string const & arg1, string const &)
1292 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1293 // remove also the arg
1296 // don't remove "-geometry"
1305 void LyX::easyParse(int & argc, char * argv[])
1307 std::map<string, cmd_helper> cmdmap;
1309 cmdmap["-dbg"] = parse_dbg;
1310 cmdmap["-help"] = parse_help;
1311 cmdmap["--help"] = parse_help;
1312 cmdmap["-version"] = parse_version;
1313 cmdmap["--version"] = parse_version;
1314 cmdmap["-sysdir"] = parse_sysdir;
1315 cmdmap["-userdir"] = parse_userdir;
1316 cmdmap["-x"] = parse_execute;
1317 cmdmap["--execute"] = parse_execute;
1318 cmdmap["-e"] = parse_export;
1319 cmdmap["--export"] = parse_export;
1320 cmdmap["-i"] = parse_import;
1321 cmdmap["--import"] = parse_import;
1322 cmdmap["-geometry"] = parse_geometry;
1324 for (int i = 1; i < argc; ++i) {
1325 std::map<string, cmd_helper>::const_iterator it
1326 = cmdmap.find(argv[i]);
1328 // don't complain if not found - may be parsed later
1329 if (it == cmdmap.end())
1332 string arg((i + 1 < argc) ? argv[i + 1] : "");
1333 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1335 int const remove = 1 + it->second(arg, arg2);
1337 // Now, remove used arguments by shifting
1338 // the following ones remove places down.
1341 for (int j = i; j < argc; ++j)
1342 argv[j] = argv[j + remove];
1347 batch_command = batch;
1351 FuncStatus getStatus(FuncRequest const & action)
1353 return LyX::ref().lyxFunc().getStatus(action);
1357 void dispatch(FuncRequest const & action)
1359 LyX::ref().lyxFunc().dispatch(action);
1363 BufferList & theBufferList()
1365 return LyX::ref().bufferList();
1369 LyXFunc & theLyXFunc()
1371 return LyX::ref().lyxFunc();
1375 LyXServer & theLyXServer()
1377 // FIXME: this should not be use_gui dependent
1378 BOOST_ASSERT(use_gui);
1379 return LyX::ref().server();
1383 LyXServerSocket & theLyXServerSocket()
1385 // FIXME: this should not be use_gui dependent
1386 BOOST_ASSERT(use_gui);
1387 return LyX::ref().socket();
1391 kb_keymap & theTopLevelKeymap()
1393 BOOST_ASSERT(use_gui);
1394 return LyX::ref().topLevelKeymap();
1398 IconvProcessor & utf8ToUcs4()
1400 return LyX::ref().iconvProcessor();