3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
20 #include "ConverterCache.h"
22 #include "buffer_funcs.h"
23 #include "bufferlist.h"
24 #include "converter.h"
27 #include "errorlist.h"
35 #include "LyXAction.h"
39 #include "lyxserver.h"
40 #include "lyxsocket.h"
41 #include "lyxtextclasslist.h"
42 #include "MenuBackend.h"
44 #include "ToolbarBackend.h"
46 #include "frontends/Alert.h"
47 #include "frontends/Application.h"
48 #include "frontends/Gui.h"
49 #include "frontends/LyXView.h"
51 #include "support/environment.h"
52 #include "support/filetools.h"
53 #include "support/fontutils.h"
54 #include "support/lyxlib.h"
55 #include "support/convert.h"
56 #include "support/os.h"
57 #include "support/package.h"
58 #include "support/path.h"
59 #include "support/systemcall.h"
60 #include "support/unicode.h"
62 #include <boost/bind.hpp>
63 #include <boost/filesystem/operations.hpp>
72 using support::addName;
73 using support::addPath;
74 using support::bformat;
75 using support::createDirectory;
76 using support::createLyXTmpDir;
77 using support::destroyDir;
78 using support::FileName;
79 using support::fileSearch;
80 using support::getEnv;
81 using support::i18nLibFileSearch;
82 using support::libFileSearch;
83 using support::package;
84 using support::prependEnvPath;
86 using support::Systemcall;
88 namespace Alert = frontend::Alert;
89 namespace os = support::os;
90 namespace fs = boost::filesystem;
97 #ifndef CXX_GLOBAL_CSTD
104 /// are we using the GUI at all?
106 * We default to true and this is changed to false when the export feature is used.
113 /// Don't try to remove the temporary directory if it has not been created
114 bool remove_tmpdir = false;
116 // Filled with the command line arguments "foo" of "-sysdir foo" or
118 string cl_system_support;
119 string cl_user_support;
121 std::string geometryArg;
123 LyX * singleton_ = 0;
125 void showFileError(string const & error)
127 Alert::warning(_("Could not read configuration file"),
128 bformat(_("Error while reading the configuration file\n%1$s.\n"
129 "Please check your installation."), from_utf8(error)));
133 void reconfigureUserLyXDir()
135 string const configure_command = package().configure_command();
137 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
138 support::Path p(package().user_support());
140 one.startscript(Systemcall::Wait, configure_command);
141 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
147 /// The main application class private implementation.
148 struct LyX::Singletons
150 Singletons(): iconv(ucs4_codeset, "UTF-8")
153 /// our function handler
156 BufferList buffer_list_;
158 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
160 boost::scoped_ptr<LyXServer> lyx_server_;
162 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
164 boost::scoped_ptr<frontend::Application> application_;
165 /// lyx session, containing lastfiles, lastfilepos, and lastopened
166 boost::scoped_ptr<Session> session_;
169 IconvProcessor iconv;
171 /// Files to load at start.
172 vector<FileName> files_to_load_;
176 frontend::Application * theApp()
179 return &singleton_->application();
192 BOOST_ASSERT(singleton_);
197 LyX const & LyX::cref()
199 BOOST_ASSERT(singleton_);
208 pimpl_.reset(new Singletons);
213 BufferList & LyX::bufferList()
215 return pimpl_->buffer_list_;
219 BufferList const & LyX::bufferList() const
221 return pimpl_->buffer_list_;
225 Session & LyX::session()
227 BOOST_ASSERT(pimpl_->session_.get());
228 return *pimpl_->session_.get();
232 Session const & LyX::session() const
234 BOOST_ASSERT(pimpl_->session_.get());
235 return *pimpl_->session_.get();
239 LyXFunc & LyX::lyxFunc()
241 return pimpl_->lyxfunc_;
245 LyXFunc const & LyX::lyxFunc() const
247 return pimpl_->lyxfunc_;
251 LyXServer & LyX::server()
253 BOOST_ASSERT(pimpl_->lyx_server_.get());
254 return *pimpl_->lyx_server_.get();
258 LyXServer const & LyX::server() const
260 BOOST_ASSERT(pimpl_->lyx_server_.get());
261 return *pimpl_->lyx_server_.get();
265 LyXServerSocket & LyX::socket()
267 BOOST_ASSERT(pimpl_->lyx_socket_.get());
268 return *pimpl_->lyx_socket_.get();
272 LyXServerSocket const & LyX::socket() const
274 BOOST_ASSERT(pimpl_->lyx_socket_.get());
275 return *pimpl_->lyx_socket_.get();
279 frontend::Application & LyX::application()
281 BOOST_ASSERT(pimpl_->application_.get());
282 return *pimpl_->application_.get();
286 frontend::Application const & LyX::application() const
288 BOOST_ASSERT(pimpl_->application_.get());
289 return *pimpl_->application_.get();
293 kb_keymap & LyX::topLevelKeymap()
295 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
296 return *pimpl_->toplevel_keymap_.get();
300 IconvProcessor & LyX::iconvProcessor()
302 return pimpl_->iconv;
306 kb_keymap const & LyX::topLevelKeymap() const
308 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
309 return *pimpl_->toplevel_keymap_.get();
313 Buffer const * const LyX::updateInset(InsetBase const * inset) const
318 Buffer const * buffer_ptr = 0;
319 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
320 vector<int>::const_iterator it = view_ids.begin();
321 vector<int>::const_iterator const end = view_ids.end();
322 for (; it != end; ++it) {
324 pimpl_->application_->gui().view(*it).updateInset(inset);
332 int LyX::exec(int & argc, char * argv[])
334 // Here we need to parse the command line. At least
335 // we need to parse for "-dbg" and "-help"
336 easyParse(argc, argv);
338 support::init_package(to_utf8(from_local8bit(argv[0])),
339 cl_system_support, cl_user_support,
340 support::top_build_dir_is_one_level_up);
343 // FIXME: create a ConsoleApplication
344 int exit_status = init(argc, argv);
352 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
357 BufferList::iterator begin = pimpl_->buffer_list_.begin();
358 BufferList::iterator end = pimpl_->buffer_list_.end();
360 bool final_success = false;
361 for (BufferList::iterator I = begin; I != end; ++I) {
363 bool success = false;
364 buf->dispatch(batch_command, &success);
365 final_success |= success;
368 return !final_success;
371 // Force adding of font path _before_ Application is initialized
372 support::addFontResources();
374 // Let the frontend parse and remove all arguments that it knows
375 pimpl_->application_.reset(createApplication(argc, argv));
379 // Parse and remove all known arguments in the LyX singleton
380 // Give an error for all remaining ones.
381 int exit_status = init(argc, argv);
383 // Kill the application object before exiting.
384 pimpl_->application_.reset();
391 /* Create a CoreApplication class that will provide the main event loop
392 * and the socket callback registering. With Qt4, only QtCore
393 * library would be needed.
394 * When this is done, a server_mode could be created and the following two
395 * line would be moved out from here.
397 // Note: socket callback must be registered after init(argc, argv)
398 // such that package().temp_dir() is properly initialized.
399 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
400 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
401 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
403 // Start the real execution loop.
404 exit_status = pimpl_->application_->exec();
408 // Restore original font resources after Application is destroyed.
409 support::restoreFontResources();
415 void LyX::prepareExit()
417 // Set a flag that we do quitting from the program,
418 // so no refreshes are necessary.
421 // close buffers first
422 pimpl_->buffer_list_.closeAll();
424 // do any other cleanup procedures now
426 lyxerr[Debug::INFO] << "Deleting tmp dir "
427 << package().temp_dir() << endl;
429 if (!destroyDir(FileName(package().temp_dir()))) {
430 docstring const msg =
431 bformat(_("Unable to remove the temporary directory %1$s"),
432 from_utf8(package().temp_dir()));
433 Alert::warning(_("Unable to remove temporary directory"), msg);
438 if (pimpl_->session_)
439 pimpl_->session_->writeFile();
440 pimpl_->session_.reset();
441 pimpl_->lyx_server_.reset();
442 pimpl_->lyx_socket_.reset();
445 // Kill the application object before exiting. This avoids crashes
446 // when exiting on Linux.
447 if (pimpl_->application_)
448 pimpl_->application_.reset();
452 void LyX::earlyExit(int status)
454 BOOST_ASSERT(pimpl_->application_.get());
455 // LyX::pimpl_::application_ is not initialised at this
456 // point so it's safe to just exit after some cleanup.
462 int LyX::init(int & argc, char * argv[])
464 // check for any spurious extra arguments
465 // other than documents
466 for (int argi = 1; argi < argc ; ++argi) {
467 if (argv[argi][0] == '-') {
469 bformat(_("Wrong command line option `%1$s'. Exiting."),
470 from_utf8(argv[argi]))) << endl;
475 // Initialization of LyX (reads lyxrc and more)
476 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
477 bool success = init();
478 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
482 for (int argi = argc - 1; argi >= 1; --argi) {
483 // get absolute path of file and add ".lyx" to
484 // the filename if necessary
485 pimpl_->files_to_load_.push_back(fileSearch(string(),
486 os::internal_path(to_utf8(from_local8bit(argv[argi]))), "lyx"));
490 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
496 void LyX::loadFiles()
498 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
499 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
501 for (; it != end; ++it) {
505 Buffer * const b = newFile(it->absFilename(), string(), true);
506 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
507 if (loadLyXFile(buf, *it)) {
508 ErrorList const & el = buf->errorList("Parse");
510 for_each(el.begin(), el.end(),
511 boost::bind(&LyX::printError, this, _1));
514 pimpl_->buffer_list_.release(buf);
519 void LyX::execBatchCommands()
521 // The advantage of doing this here is that the event loop
522 // is already started. So any need for interaction will be
526 // Execute batch commands if available
527 if (batch_command.empty())
530 lyxerr[Debug::INIT] << "About to handle -x '"
531 << batch_command << '\'' << endl;
533 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
537 void LyX::restoreGuiSession()
539 LyXView * view = newLyXView();
541 // if some files were specified at command-line we assume that the
542 // user wants to edit *these* files and not to restore the session.
543 if (!pimpl_->files_to_load_.empty()) {
544 for_each(pimpl_->files_to_load_.begin(),
545 pimpl_->files_to_load_.end(),
546 bind(&LyXView::loadLyXFile, view, _1, true));
547 // clear this list to save a few bytes of RAM
548 pimpl_->files_to_load_.clear();
549 pimpl_->session_->lastOpened().clear();
553 if (!lyxrc.load_session)
556 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
557 // do not add to the lastfile list since these files are restored from
558 // last session, and should be already there (regular files), or should
559 // not be added at all (help files).
560 for_each(lastopened.begin(), lastopened.end(),
561 bind(&LyXView::loadLyXFile, view, _1, false));
563 // clear this list to save a few bytes of RAM
564 pimpl_->session_->lastOpened().clear();
568 LyXView * LyX::newLyXView()
573 // determine windows size and position, from lyxrc and/or session
575 unsigned int width = 690;
576 unsigned int height = 510;
577 // default icon size, will be overwritten by stored session value
578 unsigned int iconSizeXY = 0;
579 bool maximize = false;
581 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
582 width = lyxrc.geometry_width;
583 height = lyxrc.geometry_height;
585 // if lyxrc returns (0,0), then use session info
587 string val = session().sessionInfo().load("WindowWidth");
589 width = convert<unsigned int>(val);
590 val = session().sessionInfo().load("WindowHeight");
592 height = convert<unsigned int>(val);
593 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
595 val = session().sessionInfo().load("IconSizeXY");
597 iconSizeXY = convert<unsigned int>(val);
600 // if user wants to restore window position
603 if (lyxrc.geometry_xysaved) {
604 string val = session().sessionInfo().load("WindowPosX");
606 posx = convert<int>(val);
607 val = session().sessionInfo().load("WindowPosY");
609 posy = convert<int>(val);
612 if (!geometryArg.empty())
618 // create the main window
619 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
627 The SIGHUP signal does not exist on Windows and does not need to be handled.
629 Windows handles SIGFPE and SIGSEGV signals as expected.
631 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
632 cause a new thread to be spawned. This may well result in unexpected
633 behaviour by the single-threaded LyX.
635 SIGTERM signals will come only from another process actually sending
636 that signal using 'raise' in Windows' POSIX compatability layer. It will
637 not come from the general "terminate process" methods that everyone
638 actually uses (and which can't be trapped). Killing an app 'politely' on
639 Windows involves first sending a WM_CLOSE message, something that is
640 caught already by the Qt frontend.
642 For more information see:
644 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
645 ...signals are mostly useless on Windows for a variety of reasons that are
648 'UNIX Application Migration Guide, Chapter 9'
649 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
651 'How To Terminate an Application "Cleanly" in Win32'
652 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
656 static void error_handler(int err_sig)
658 // Throw away any signals other than the first one received.
659 static sig_atomic_t handling_error = false;
662 handling_error = true;
664 // We have received a signal indicating a fatal error, so
665 // try and save the data ASAP.
666 LyX::cref().emergencyCleanup();
668 // These lyxerr calls may or may not work:
670 // Signals are asynchronous, so the main program may be in a very
671 // fragile state when a signal is processed and thus while a signal
672 // handler function executes.
673 // In general, therefore, we should avoid performing any
674 // I/O operations or calling most library and system functions from
677 // This shouldn't matter here, however, as we've already invoked
682 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
686 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
689 lyxerr << "\nlyx: SIGSEGV signal caught\n"
690 "Sorry, you have found a bug in LyX. "
691 "Please read the bug-reporting instructions "
692 "in Help->Introduction and send us a bug report, "
693 "if necessary. Thanks !\nBye." << endl;
701 // Deinstall the signal handlers
703 signal(SIGHUP, SIG_DFL);
705 signal(SIGINT, SIG_DFL);
706 signal(SIGFPE, SIG_DFL);
707 signal(SIGSEGV, SIG_DFL);
708 signal(SIGTERM, SIG_DFL);
711 if (err_sig == SIGSEGV ||
712 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
714 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
723 void LyX::printError(ErrorItem const & ei)
725 docstring tmp = _("LyX: ") + ei.error + char_type(':')
727 std::cerr << to_utf8(tmp) << std::endl;
731 void LyX::initGuiFont()
733 if (lyxrc.roman_font_name.empty())
734 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
736 if (lyxrc.sans_font_name.empty())
737 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
739 if (lyxrc.typewriter_font_name.empty())
740 lyxrc.typewriter_font_name
741 = pimpl_->application_->typewriterFontName();
748 signal(SIGHUP, error_handler);
750 signal(SIGFPE, error_handler);
751 signal(SIGSEGV, error_handler);
752 signal(SIGINT, error_handler);
753 signal(SIGTERM, error_handler);
754 // SIGPIPE can be safely ignored.
756 lyxrc.tempdir_path = package().temp_dir();
757 lyxrc.document_path = package().document_dir();
759 if (lyxrc.template_path.empty()) {
760 lyxrc.template_path = addPath(package().system_support(),
765 // Read configuration files
768 // This one may have been distributed along with LyX.
769 if (!readRcFile("lyxrc.dist"))
772 // Set the PATH correctly.
773 #if !defined (USE_POSIX_PACKAGING)
774 // Add the directory containing the LyX executable to the path
775 // so that LyX can find things like tex2lyx.
776 if (package().build_support().empty())
777 prependEnvPath("PATH", package().binary_dir());
779 if (!lyxrc.path_prefix.empty())
780 prependEnvPath("PATH", lyxrc.path_prefix);
782 // Check that user LyX directory is ok.
783 if (queryUserLyXDir(package().explicit_user_support()))
784 reconfigureUserLyXDir();
786 // no need for a splash when there is no GUI
791 // This one is generated in user_support directory by lib/configure.py.
792 if (!readRcFile("lyxrc.defaults"))
795 // Query the OS to know what formats are viewed natively
796 formats.setAutoOpen();
798 // Read lyxrc.dist again to be able to override viewer auto-detection.
799 readRcFile("lyxrc.dist");
801 system_lyxrc = lyxrc;
802 system_formats = formats;
803 system_converters = converters;
804 system_movers = movers;
805 system_lcolor = lcolor;
807 // This one is edited through the preferences dialog.
808 if (!readRcFile("preferences"))
811 if (!readEncodingsFile("encodings"))
813 if (!readLanguagesFile("languages"))
817 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
823 pimpl_->toplevel_keymap_.reset(new kb_keymap);
824 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
825 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
827 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
830 if (!readUIFile(lyxrc.ui_file))
834 if (lyxerr.debugging(Debug::LYXRC))
837 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
838 if (!lyxrc.path_prefix.empty())
839 prependEnvPath("PATH", lyxrc.path_prefix);
841 FileName const document_path(lyxrc.document_path);
842 if (fs::exists(document_path.toFilesystemEncoding()) &&
843 fs::is_directory(document_path.toFilesystemEncoding()))
844 package().document_dir() = lyxrc.document_path;
846 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
847 if (package().temp_dir().empty()) {
848 Alert::error(_("Could not create temporary directory"),
849 bformat(_("Could not create a temporary directory in\n"
850 "%1$s. Make sure that this\n"
851 "path exists and is writable and try again."),
852 from_utf8(lyxrc.tempdir_path)));
853 // createLyXTmpDir() tries sufficiently hard to create a
854 // usable temp dir, so the probability to come here is
855 // close to zero. We therefore don't try to overcome this
856 // problem with e.g. asking the user for a new path and
857 // trying again but simply exit.
860 remove_tmpdir = true;
862 if (lyxerr.debugging(Debug::INIT)) {
863 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
866 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
867 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
869 // This must happen after package initialization and after lyxrc is
870 // read, therefore it can't be done by a static object.
871 ConverterCache::init();
877 void LyX::defaultKeyBindings(kb_keymap * kbmap)
879 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
880 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
881 kbmap->bind("Up", FuncRequest(LFUN_UP));
882 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
884 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
885 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
886 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
887 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
889 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
890 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
891 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
892 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
894 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
895 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
897 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
898 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
900 // kbmap->bindings to enable the use of the numeric keypad
902 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
903 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
904 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
905 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
906 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
915 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
916 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
917 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
918 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
919 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
920 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
921 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
922 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
923 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
924 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
925 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
929 void LyX::emergencyCleanup() const
931 // what to do about tmpfiles is non-obvious. we would
932 // like to delete any we find, but our lyxdir might
933 // contain documents etc. which might be helpful on
936 pimpl_->buffer_list_.emergencyWriteAll();
938 if (pimpl_->lyx_server_)
939 pimpl_->lyx_server_->emergencyCleanup();
940 pimpl_->lyx_server_.reset();
941 pimpl_->lyx_socket_.reset();
946 void LyX::deadKeyBindings(kb_keymap * kbmap)
948 // bindKeyings for transparent handling of deadkeys
949 // The keysyms are gotten from XFree86 X11R6
950 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
951 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
952 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
953 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
954 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
955 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
956 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
957 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
958 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
959 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
960 // nothing with this name
961 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
962 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
963 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
964 // nothing with this name either...
965 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
966 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
967 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
968 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
974 // return true if file does not exist or is older than configure.py.
975 bool needsUpdate(string const & file)
977 // We cannot initialize configure_script directly because the package
978 // is not initialized yet when static objects are constructed.
979 static string configure_script;
980 static bool firstrun = true;
982 configure_script = FileName(addName(
983 package().system_support(),
984 "configure.py")).toFilesystemEncoding();
988 string const absfile = FileName(addName(
989 package().user_support(), file)).toFilesystemEncoding();
990 return (! fs::exists(absfile))
991 || (fs::last_write_time(configure_script)
992 > fs::last_write_time(absfile));
998 bool LyX::queryUserLyXDir(bool explicit_userdir)
1000 // Does user directory exist?
1001 string const user_support =
1002 FileName(package().user_support()).toFilesystemEncoding();
1003 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1004 first_start = false;
1006 return needsUpdate("lyxrc.defaults")
1007 || needsUpdate("textclass.lst")
1008 || needsUpdate("packages.lst");
1011 first_start = !explicit_userdir;
1013 // If the user specified explicitly a directory, ask whether
1014 // to create it. If the user says "no", then exit.
1015 if (explicit_userdir &&
1017 _("Missing user LyX directory"),
1018 bformat(_("You have specified a non-existent user "
1019 "LyX directory, %1$s.\n"
1020 "It is needed to keep your own configuration."),
1021 from_utf8(package().user_support())),
1023 _("&Create directory"),
1025 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1026 earlyExit(EXIT_FAILURE);
1029 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1030 from_utf8(package().user_support())))
1033 if (!createDirectory(package().user_support(), 0755)) {
1034 // Failed, so let's exit.
1035 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1037 earlyExit(EXIT_FAILURE);
1044 bool LyX::readRcFile(string const & name)
1046 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1048 FileName const lyxrc_path = libFileSearch(string(), name);
1049 if (!lyxrc_path.empty()) {
1051 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1053 if (lyxrc.read(lyxrc_path) < 0) {
1054 showFileError(name);
1058 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1064 // Read the ui file `name'
1065 bool LyX::readUIFile(string const & name)
1075 struct keyword_item uitags[ui_last - 1] = {
1076 { "include", ui_include },
1077 { "menuset", ui_menuset },
1078 { "toolbar", ui_toolbar },
1079 { "toolbars", ui_toolbars }
1082 // Ensure that a file is read only once (prevents include loops)
1083 static std::list<string> uifiles;
1084 std::list<string>::const_iterator it = uifiles.begin();
1085 std::list<string>::const_iterator end = uifiles.end();
1086 it = std::find(it, end, name);
1088 lyxerr[Debug::INIT] << "UI file '" << name
1089 << "' has been read already. "
1090 << "Is this an include loop?"
1095 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1097 FileName const ui_path = libFileSearch("ui", name, "ui");
1099 if (ui_path.empty()) {
1100 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1101 showFileError(name);
1104 uifiles.push_back(name);
1106 lyxerr[Debug::INIT] << "Found " << name
1107 << " in " << ui_path << endl;
1108 LyXLex lex(uitags, ui_last - 1);
1109 lex.setFile(ui_path);
1111 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1115 if (lyxerr.debugging(Debug::PARSER))
1116 lex.printTable(lyxerr);
1118 while (lex.isOK()) {
1119 switch (lex.lex()) {
1122 string const file = lex.getString();
1123 if (!readUIFile(file))
1128 menubackend.read(lex);
1132 toolbarbackend.read(lex);
1136 toolbarbackend.readToolbars(lex);
1140 if (!rtrim(lex.getString()).empty())
1141 lex.printError("LyX::ReadUIFile: "
1142 "Unknown menu tag: `$$Token'");
1150 // Read the languages file `name'
1151 bool LyX::readLanguagesFile(string const & name)
1153 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1155 FileName const lang_path = libFileSearch(string(), name);
1156 if (lang_path.empty()) {
1157 showFileError(name);
1160 languages.read(lang_path);
1165 // Read the encodings file `name'
1166 bool LyX::readEncodingsFile(string const & name)
1168 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1170 FileName const enc_path = libFileSearch(string(), name);
1171 if (enc_path.empty()) {
1172 showFileError(name);
1175 encodings.read(enc_path);
1184 /// return the the number of arguments consumed
1185 typedef boost::function<int(string const &, string const &)> cmd_helper;
1187 int parse_dbg(string const & arg, string const &)
1190 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1191 Debug::showTags(lyxerr);
1194 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1196 lyxerr.level(Debug::value(arg));
1197 Debug::showLevel(lyxerr, lyxerr.level());
1202 int parse_help(string const &, string const &)
1205 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1206 "Command line switches (case sensitive):\n"
1207 "\t-help summarize LyX usage\n"
1208 "\t-userdir dir set user directory to dir\n"
1209 "\t-sysdir dir set system directory to dir\n"
1210 "\t-geometry WxH+X+Y set geometry of the main window\n"
1211 "\t-dbg feature[,feature]...\n"
1212 " select the features to debug.\n"
1213 " Type `lyx -dbg' to see the list of features\n"
1214 "\t-x [--execute] command\n"
1215 " where command is a lyx command.\n"
1216 "\t-e [--export] fmt\n"
1217 " where fmt is the export format of choice.\n"
1218 "\t-i [--import] fmt file.xxx\n"
1219 " where fmt is the import format of choice\n"
1220 " and file.xxx is the file to be imported.\n"
1221 "\t-version summarize version and build info\n"
1222 "Check the LyX man page for more details.")) << endl;
1227 int parse_version(string const &, string const &)
1229 lyxerr << "LyX " << lyx_version
1230 << " (" << lyx_release_date << ")" << endl;
1231 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1233 lyxerr << lyx_version_info << endl;
1238 int parse_sysdir(string const & arg, string const &)
1241 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1244 cl_system_support = arg;
1248 int parse_userdir(string const & arg, string const &)
1251 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1254 cl_user_support = arg;
1258 int parse_execute(string const & arg, string const &)
1261 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1268 int parse_export(string const & type, string const &)
1271 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1272 "--export switch")) << endl;
1275 batch = "buffer-export " + type;
1280 int parse_import(string const & type, string const & file)
1283 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1284 "--import switch")) << endl;
1288 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1292 batch = "buffer-import " + type + ' ' + file;
1296 int parse_geometry(string const & arg1, string const &)
1299 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1300 // remove also the arg
1303 // don't remove "-geometry"
1312 void LyX::easyParse(int & argc, char * argv[])
1314 std::map<string, cmd_helper> cmdmap;
1316 cmdmap["-dbg"] = parse_dbg;
1317 cmdmap["-help"] = parse_help;
1318 cmdmap["--help"] = parse_help;
1319 cmdmap["-version"] = parse_version;
1320 cmdmap["--version"] = parse_version;
1321 cmdmap["-sysdir"] = parse_sysdir;
1322 cmdmap["-userdir"] = parse_userdir;
1323 cmdmap["-x"] = parse_execute;
1324 cmdmap["--execute"] = parse_execute;
1325 cmdmap["-e"] = parse_export;
1326 cmdmap["--export"] = parse_export;
1327 cmdmap["-i"] = parse_import;
1328 cmdmap["--import"] = parse_import;
1329 cmdmap["-geometry"] = parse_geometry;
1331 for (int i = 1; i < argc; ++i) {
1332 std::map<string, cmd_helper>::const_iterator it
1333 = cmdmap.find(argv[i]);
1335 // don't complain if not found - may be parsed later
1336 if (it == cmdmap.end())
1339 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1340 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1342 int const remove = 1 + it->second(arg, arg2);
1344 // Now, remove used arguments by shifting
1345 // the following ones remove places down.
1348 for (int j = i; j < argc; ++j)
1349 argv[j] = argv[j + remove];
1354 batch_command = batch;
1358 FuncStatus getStatus(FuncRequest const & action)
1360 return LyX::ref().lyxFunc().getStatus(action);
1364 void dispatch(FuncRequest const & action)
1366 LyX::ref().lyxFunc().dispatch(action);
1370 BufferList & theBufferList()
1372 return LyX::ref().bufferList();
1376 LyXFunc & theLyXFunc()
1378 return LyX::ref().lyxFunc();
1382 LyXServer & theLyXServer()
1384 // FIXME: this should not be use_gui dependent
1385 BOOST_ASSERT(use_gui);
1386 return LyX::ref().server();
1390 LyXServerSocket & theLyXServerSocket()
1392 // FIXME: this should not be use_gui dependent
1393 BOOST_ASSERT(use_gui);
1394 return LyX::ref().socket();
1398 kb_keymap & theTopLevelKeymap()
1400 BOOST_ASSERT(use_gui);
1401 return LyX::ref().topLevelKeymap();
1405 IconvProcessor & utf8ToUcs4()
1407 return LyX::ref().iconvProcessor();