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 /// are we using the GUI at all?
105 * We default to true and this is changed to false when the export feature is used.
112 // Filled with the command line arguments "foo" of "-sysdir foo" or
114 string cl_system_support;
115 string cl_user_support;
117 std::string geometryArg;
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;
169 frontend::Application * theApp()
172 return &singleton_->application();
180 // Static data are not treated in the same way at all on the Mac (and
181 // the LyX singleton has static methods). This is the reason why the
182 // exit command on the Mac bypasses our dispatch machinery altogether.
183 // On Linux and Windows we won't pass a second time through quit()
184 // because quitting will already be set to true.
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(argv[0], cl_system_support, cl_user_support,
339 support::top_build_dir_is_one_level_up);
341 vector<string> files;
344 // FIXME: create a ConsoleApplication
345 int exit_status = loadFiles(argc, argv, files);
351 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
356 BufferList::iterator begin = pimpl_->buffer_list_.begin();
357 BufferList::iterator end = pimpl_->buffer_list_.end();
359 bool final_success = false;
360 for (BufferList::iterator I = begin; I != end; ++I) {
362 bool success = false;
363 buf->dispatch(batch_command, &success);
364 final_success |= success;
367 return !final_success;
370 // Force adding of font path _before_ Application is initialized
371 support::addFontResources();
373 // Let the frontend parse and remove all arguments that it knows
374 pimpl_->application_.reset(createApplication(argc, argv));
378 // Parse and remove all known arguments in the LyX singleton
379 // Give an error for all remaining ones.
380 int exit_status = loadFiles(argc, argv, files);
382 // Kill the application object before exiting.
383 pimpl_->application_.reset();
389 restoreGuiSession(files);
390 // Start the real execution loop.
393 /* Create a CoreApplication class that will provide the main event loop
394 * and the socket callback registering. With Qt4, only QtCore
395 * library would be needed.
396 * When this is done, a server_mode could be created and the following two
397 * line would be moved out from here.
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 exit_status = pimpl_->application_->exec();
405 // FIXME: Do we still need this reset?
406 // I assume it is the reason for strange Mac crashs
407 // Test by reverting rev 16110 (Peter)
408 // Kill the application object before exiting. This avoid crash
410 pimpl_->application_.reset();
412 // Restore original font resources after Application is destroyed.
413 support::restoreFontResources();
419 void LyX::prepareExit()
421 // Set a flag that we do quitting from the program,
422 // so no refreshes are necessary.
425 // close buffers first
426 pimpl_->buffer_list_.closeAll();
428 // do any other cleanup procedures now
429 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
431 // Prevent the deletion of /tmp if LyX was called with invalid
432 // arguments. Does not work on windows.
433 // FIXME: Fix the real bug instead.
434 if (package().temp_dir() == "/tmp") {
435 lyxerr << "Not deleting /tmp." << endl;
439 if (!destroyDir(package().temp_dir())) {
440 docstring const msg =
441 bformat(_("Unable to remove the temporary directory %1$s"),
442 from_utf8(package().temp_dir()));
443 Alert::warning(_("Unable to remove temporary directory"), msg);
448 void LyX::earlyExit(int status)
450 BOOST_ASSERT(pimpl_->application_.get());
451 // LyX::pimpl_::application_ is not initialised at this
452 // point so it's safe to just exit after some cleanup.
460 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
464 if (pimpl_->session_)
465 pimpl_->session_->writeFile();
466 pimpl_->lyx_server_.reset();
467 pimpl_->lyx_socket_.reset();
468 if (pimpl_->application_)
469 pimpl_->application_->exit(0);
474 int LyX::loadFiles(int & argc, char * argv[],
475 vector<string> & files)
477 // check for any spurious extra arguments
478 // other than documents
479 for (int argi = 1; argi < argc ; ++argi) {
480 if (argv[argi][0] == '-') {
482 bformat(_("Wrong command line option `%1$s'. Exiting."),
483 from_utf8(argv[argi]))) << endl;
488 // Initialization of LyX (reads lyxrc and more)
489 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
490 bool success = init();
491 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
495 for (int argi = argc - 1; argi >= 1; --argi) {
496 // check for any remaining extra arguments other than
497 // document file names. These will be passed out to the
499 if (argv[argi][0] == '-')
501 files.push_back(os::internal_path(argv[argi]));
505 files.push_back(i18nLibFileSearch("examples", "splash.lyx").absFilename());
507 Buffer * last_loaded = 0;
509 vector<string>::const_iterator it = files.begin();
510 vector<string>::const_iterator end = files.end();
512 for (; it != end; ++it) {
513 // get absolute path of file and add ".lyx" to
514 // the filename if necessary
515 string s = fileSearch(string(), *it, "lyx").absFilename();
517 Buffer * const b = newFile(*it, string(), true);
521 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
522 if (loadLyXFile(buf, s)) {
524 ErrorList const & el = buf->errorList("Parse");
526 for_each(el.begin(), el.end(),
527 boost::bind(&LyX::printError, this, _1));
530 pimpl_->buffer_list_.release(buf);
534 files.clear(); // the files are already loaded
540 void LyX::execBatchCommands()
542 // Execute batch commands if available
543 if (batch_command.empty())
546 lyxerr[Debug::INIT] << "About to handle -x '"
547 << batch_command << '\'' << endl;
549 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
553 void LyX::restoreGuiSession(vector<string> const & files)
555 LyXView * view = newLyXView();
558 for_each(files.begin(), files.end(),
559 bind(&LyXView::loadLyXFile, view, _1, true));
561 // if a file is specified, I assume that user wants to edit *that* file
562 if (files.empty() && lyxrc.load_session) {
563 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
564 // do not add to the lastfile list since these files are restored from
565 // last seesion, and should be already there (regular files), or should
566 // not be added at all (help files).
567 for_each(lastopened.begin(), lastopened.end(),
568 bind(&LyXView::loadLyXFile, view, _1, false));
570 // clear this list to save a few bytes of RAM
571 pimpl_->session_->lastOpened().clear();
575 LyXView * LyX::newLyXView()
580 // determine windows size and position, from lyxrc and/or session
582 unsigned int width = 690;
583 unsigned int height = 510;
584 // default icon size, will be overwritten by stored session value
585 unsigned int iconSizeXY = 0;
586 bool maximize = false;
588 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
589 width = lyxrc.geometry_width;
590 height = lyxrc.geometry_height;
592 // if lyxrc returns (0,0), then use session info
594 string val = session().sessionInfo().load("WindowWidth");
596 width = convert<unsigned int>(val);
597 val = session().sessionInfo().load("WindowHeight");
599 height = convert<unsigned int>(val);
600 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
602 val = session().sessionInfo().load("IconSizeXY");
604 iconSizeXY = convert<unsigned int>(val);
607 // if user wants to restore window position
610 if (lyxrc.geometry_xysaved) {
611 string val = session().sessionInfo().load("WindowPosX");
613 posx = convert<int>(val);
614 val = session().sessionInfo().load("WindowPosY");
616 posy = convert<int>(val);
619 if (!geometryArg.empty())
625 // create the main window
626 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
634 The SIGHUP signal does not exist on Windows and does not need to be handled.
636 Windows handles SIGFPE and SIGSEGV signals as expected.
638 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
639 cause a new thread to be spawned. This may well result in unexpected
640 behaviour by the single-threaded LyX.
642 SIGTERM signals will come only from another process actually sending
643 that signal using 'raise' in Windows' POSIX compatability layer. It will
644 not come from the general "terminate process" methods that everyone
645 actually uses (and which can't be trapped). Killing an app 'politely' on
646 Windows involves first sending a WM_CLOSE message, something that is
647 caught already by the Qt frontend.
649 For more information see:
651 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
652 ...signals are mostly useless on Windows for a variety of reasons that are
655 'UNIX Application Migration Guide, Chapter 9'
656 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
658 'How To Terminate an Application "Cleanly" in Win32'
659 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
663 static void error_handler(int err_sig)
665 // Throw away any signals other than the first one received.
666 static sig_atomic_t handling_error = false;
669 handling_error = true;
671 // We have received a signal indicating a fatal error, so
672 // try and save the data ASAP.
673 LyX::cref().emergencyCleanup();
675 // These lyxerr calls may or may not work:
677 // Signals are asynchronous, so the main program may be in a very
678 // fragile state when a signal is processed and thus while a signal
679 // handler function executes.
680 // In general, therefore, we should avoid performing any
681 // I/O operations or calling most library and system functions from
684 // This shouldn't matter here, however, as we've already invoked
689 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
693 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
696 lyxerr << "\nlyx: SIGSEGV signal caught\n"
697 "Sorry, you have found a bug in LyX. "
698 "Please read the bug-reporting instructions "
699 "in Help->Introduction and send us a bug report, "
700 "if necessary. Thanks !\nBye." << endl;
708 // Deinstall the signal handlers
710 signal(SIGHUP, SIG_DFL);
712 signal(SIGINT, SIG_DFL);
713 signal(SIGFPE, SIG_DFL);
714 signal(SIGSEGV, SIG_DFL);
715 signal(SIGTERM, SIG_DFL);
718 if (err_sig == SIGSEGV ||
719 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
721 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
730 void LyX::printError(ErrorItem const & ei)
732 docstring tmp = _("LyX: ") + ei.error + char_type(':')
734 std::cerr << to_utf8(tmp) << std::endl;
738 void LyX::initGuiFont()
740 if (lyxrc.roman_font_name.empty())
741 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
743 if (lyxrc.sans_font_name.empty())
744 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
746 if (lyxrc.typewriter_font_name.empty())
747 lyxrc.typewriter_font_name
748 = pimpl_->application_->typewriterFontName();
755 signal(SIGHUP, error_handler);
757 signal(SIGFPE, error_handler);
758 signal(SIGSEGV, error_handler);
759 signal(SIGINT, error_handler);
760 signal(SIGTERM, error_handler);
761 // SIGPIPE can be safely ignored.
763 lyxrc.tempdir_path = package().temp_dir();
764 lyxrc.document_path = package().document_dir();
766 if (lyxrc.template_path.empty()) {
767 lyxrc.template_path = addPath(package().system_support(),
772 // Read configuration files
775 // This one may have been distributed along with LyX.
776 if (!readRcFile("lyxrc.dist"))
779 // Set the PATH correctly.
780 #if !defined (USE_POSIX_PACKAGING)
781 // Add the directory containing the LyX executable to the path
782 // so that LyX can find things like tex2lyx.
783 if (package().build_support().empty())
784 prependEnvPath("PATH", package().binary_dir());
786 if (!lyxrc.path_prefix.empty())
787 prependEnvPath("PATH", lyxrc.path_prefix);
789 // Check that user LyX directory is ok.
790 if (queryUserLyXDir(package().explicit_user_support()))
791 reconfigureUserLyXDir();
793 // no need for a splash when there is no GUI
798 // This one is generated in user_support directory by lib/configure.py.
799 if (!readRcFile("lyxrc.defaults"))
802 // Query the OS to know what formats are viewed natively
803 formats.setAutoOpen();
805 // Read lyxrc.dist again to be able to override viewer auto-detection.
806 readRcFile("lyxrc.dist");
808 system_lyxrc = lyxrc;
809 system_formats = formats;
810 system_converters = converters;
811 system_movers = movers;
812 system_lcolor = lcolor;
814 // This one is edited through the preferences dialog.
815 if (!readRcFile("preferences"))
818 if (!readEncodingsFile("encodings"))
820 if (!readLanguagesFile("languages"))
824 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
830 pimpl_->toplevel_keymap_.reset(new kb_keymap);
831 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
832 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
834 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
837 if (!readUIFile(lyxrc.ui_file))
841 if (lyxerr.debugging(Debug::LYXRC))
844 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
845 if (!lyxrc.path_prefix.empty())
846 prependEnvPath("PATH", lyxrc.path_prefix);
848 if (fs::exists(lyxrc.document_path) &&
849 fs::is_directory(lyxrc.document_path))
850 package().document_dir() = lyxrc.document_path;
852 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
853 if (package().temp_dir().empty()) {
854 Alert::error(_("Could not create temporary directory"),
855 bformat(_("Could not create a temporary directory in\n"
856 "%1$s. Make sure that this\n"
857 "path exists and is writable and try again."),
858 from_utf8(lyxrc.tempdir_path)));
859 // createLyXTmpDir() tries sufficiently hard to create a
860 // usable temp dir, so the probability to come here is
861 // close to zero. We therefore don't try to overcome this
862 // problem with e.g. asking the user for a new path and
863 // trying again but simply exit.
867 if (lyxerr.debugging(Debug::INIT)) {
868 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
871 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
872 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
874 // This must happen after package initialization and after lyxrc is
875 // read, therefore it can't be done by a static object.
876 ConverterCache::init();
882 void LyX::defaultKeyBindings(kb_keymap * kbmap)
884 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
885 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
886 kbmap->bind("Up", FuncRequest(LFUN_UP));
887 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
889 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
890 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
891 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
892 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
894 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
895 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
896 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
897 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
899 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
900 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
902 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
903 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
905 // kbmap->bindings to enable the use of the numeric keypad
907 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
909 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
910 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
915 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
916 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
917 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
918 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
919 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
920 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
921 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
922 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
923 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
924 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
925 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
926 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
927 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
928 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
929 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
930 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
934 void LyX::emergencyCleanup() const
936 // what to do about tmpfiles is non-obvious. we would
937 // like to delete any we find, but our lyxdir might
938 // contain documents etc. which might be helpful on
941 pimpl_->buffer_list_.emergencyWriteAll();
943 if (pimpl_->lyx_server_)
944 pimpl_->lyx_server_->emergencyCleanup();
945 pimpl_->lyx_server_.reset();
946 pimpl_->lyx_socket_.reset();
951 void LyX::deadKeyBindings(kb_keymap * kbmap)
953 // bindKeyings for transparent handling of deadkeys
954 // The keysyms are gotten from XFree86 X11R6
955 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
956 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
957 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
958 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
959 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
960 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
961 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
962 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
963 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
964 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
965 // nothing with this name
966 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
967 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
968 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
969 // nothing with this name either...
970 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
971 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
972 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
973 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
979 // return true if file does not exist or is older than configure.py.
980 bool needsUpdate(string const & file)
982 static string const configure_script =
983 addName(package().system_support(), "configure.py");
984 string const absfile =
985 addName(package().user_support(), file);
987 return (! fs::exists(absfile))
988 || (fs::last_write_time(configure_script)
989 > fs::last_write_time(absfile));
995 bool LyX::queryUserLyXDir(bool explicit_userdir)
997 // Does user directory exist?
998 if (fs::exists(package().user_support()) &&
999 fs::is_directory(package().user_support())) {
1000 first_start = false;
1002 return needsUpdate("lyxrc.defaults")
1003 || needsUpdate("textclass.lst")
1004 || needsUpdate("packages.lst");
1007 first_start = !explicit_userdir;
1009 // If the user specified explicitly a directory, ask whether
1010 // to create it. If the user says "no", then exit.
1011 if (explicit_userdir &&
1013 _("Missing user LyX directory"),
1014 bformat(_("You have specified a non-existent user "
1015 "LyX directory, %1$s.\n"
1016 "It is needed to keep your own configuration."),
1017 from_utf8(package().user_support())),
1019 _("&Create directory"),
1021 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1022 earlyExit(EXIT_FAILURE);
1025 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1026 from_utf8(package().user_support())))
1029 if (!createDirectory(package().user_support(), 0755)) {
1030 // Failed, so let's exit.
1031 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1033 earlyExit(EXIT_FAILURE);
1040 bool LyX::readRcFile(string const & name)
1042 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1044 FileName const lyxrc_path = libFileSearch(string(), name);
1045 if (!lyxrc_path.empty()) {
1047 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1049 if (lyxrc.read(lyxrc_path) < 0) {
1050 showFileError(name);
1054 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1060 // Read the ui file `name'
1061 bool LyX::readUIFile(string const & name)
1071 struct keyword_item uitags[ui_last - 1] = {
1072 { "include", ui_include },
1073 { "menuset", ui_menuset },
1074 { "toolbar", ui_toolbar },
1075 { "toolbars", ui_toolbars }
1078 // Ensure that a file is read only once (prevents include loops)
1079 static std::list<string> uifiles;
1080 std::list<string>::const_iterator it = uifiles.begin();
1081 std::list<string>::const_iterator end = uifiles.end();
1082 it = std::find(it, end, name);
1084 lyxerr[Debug::INIT] << "UI file '" << name
1085 << "' has been read already. "
1086 << "Is this an include loop?"
1091 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1093 FileName const ui_path = libFileSearch("ui", name, "ui");
1095 if (ui_path.empty()) {
1096 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1097 showFileError(name);
1100 uifiles.push_back(name);
1102 lyxerr[Debug::INIT] << "Found " << name
1103 << " in " << ui_path << endl;
1104 LyXLex lex(uitags, ui_last - 1);
1105 lex.setFile(ui_path);
1107 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1111 if (lyxerr.debugging(Debug::PARSER))
1112 lex.printTable(lyxerr);
1114 while (lex.isOK()) {
1115 switch (lex.lex()) {
1118 string const file = lex.getString();
1119 if (!readUIFile(file))
1124 menubackend.read(lex);
1128 toolbarbackend.read(lex);
1132 toolbarbackend.readToolbars(lex);
1136 if (!rtrim(lex.getString()).empty())
1137 lex.printError("LyX::ReadUIFile: "
1138 "Unknown menu tag: `$$Token'");
1146 // Read the languages file `name'
1147 bool LyX::readLanguagesFile(string const & name)
1149 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1151 FileName const lang_path = libFileSearch(string(), name);
1152 if (lang_path.empty()) {
1153 showFileError(name);
1156 languages.read(lang_path);
1161 // Read the encodings file `name'
1162 bool LyX::readEncodingsFile(string const & name)
1164 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1166 FileName const enc_path = libFileSearch(string(), name);
1167 if (enc_path.empty()) {
1168 showFileError(name);
1171 encodings.read(enc_path);
1180 /// return the the number of arguments consumed
1181 typedef boost::function<int(string const &, string const &)> cmd_helper;
1183 int parse_dbg(string const & arg, string const &)
1186 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1187 Debug::showTags(lyxerr);
1190 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1192 lyxerr.level(Debug::value(arg));
1193 Debug::showLevel(lyxerr, lyxerr.level());
1198 int parse_help(string const &, string const &)
1201 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1202 "Command line switches (case sensitive):\n"
1203 "\t-help summarize LyX usage\n"
1204 "\t-userdir dir set user directory to dir\n"
1205 "\t-sysdir dir set system directory to dir\n"
1206 "\t-geometry WxH+X+Y set geometry of the main window\n"
1207 "\t-dbg feature[,feature]...\n"
1208 " select the features to debug.\n"
1209 " Type `lyx -dbg' to see the list of features\n"
1210 "\t-x [--execute] command\n"
1211 " where command is a lyx command.\n"
1212 "\t-e [--export] fmt\n"
1213 " where fmt is the export format of choice.\n"
1214 "\t-i [--import] fmt file.xxx\n"
1215 " where fmt is the import format of choice\n"
1216 " and file.xxx is the file to be imported.\n"
1217 "\t-version summarize version and build info\n"
1218 "Check the LyX man page for more details.")) << endl;
1223 int parse_version(string const &, string const &)
1225 lyxerr << "LyX " << lyx_version
1226 << " (" << lyx_release_date << ")" << endl;
1227 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1229 lyxerr << lyx_version_info << endl;
1234 int parse_sysdir(string const & arg, string const &)
1237 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1240 cl_system_support = arg;
1244 int parse_userdir(string const & arg, string const &)
1247 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1250 cl_user_support = arg;
1254 int parse_execute(string const & arg, string const &)
1257 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1264 int parse_export(string const & type, string const &)
1267 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1268 "--export switch")) << endl;
1271 batch = "buffer-export " + type;
1276 int parse_import(string const & type, string const & file)
1279 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1280 "--import switch")) << endl;
1284 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1288 batch = "buffer-import " + type + ' ' + file;
1292 int parse_geometry(string const & arg1, string const &)
1295 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1296 // remove also the arg
1299 // don't remove "-geometry"
1308 void LyX::easyParse(int & argc, char * argv[])
1310 std::map<string, cmd_helper> cmdmap;
1312 cmdmap["-dbg"] = parse_dbg;
1313 cmdmap["-help"] = parse_help;
1314 cmdmap["--help"] = parse_help;
1315 cmdmap["-version"] = parse_version;
1316 cmdmap["--version"] = parse_version;
1317 cmdmap["-sysdir"] = parse_sysdir;
1318 cmdmap["-userdir"] = parse_userdir;
1319 cmdmap["-x"] = parse_execute;
1320 cmdmap["--execute"] = parse_execute;
1321 cmdmap["-e"] = parse_export;
1322 cmdmap["--export"] = parse_export;
1323 cmdmap["-i"] = parse_import;
1324 cmdmap["--import"] = parse_import;
1325 cmdmap["-geometry"] = parse_geometry;
1327 for (int i = 1; i < argc; ++i) {
1328 std::map<string, cmd_helper>::const_iterator it
1329 = cmdmap.find(argv[i]);
1331 // don't complain if not found - may be parsed later
1332 if (it == cmdmap.end())
1335 string arg((i + 1 < argc) ? argv[i + 1] : "");
1336 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1338 int const remove = 1 + it->second(arg, arg2);
1340 // Now, remove used arguments by shifting
1341 // the following ones remove places down.
1344 for (int j = i; j < argc; ++j)
1345 argv[j] = argv[j + remove];
1350 batch_command = batch;
1354 FuncStatus getStatus(FuncRequest const & action)
1356 return LyX::ref().lyxFunc().getStatus(action);
1360 void dispatch(FuncRequest const & action)
1362 LyX::ref().lyxFunc().dispatch(action);
1366 BufferList & theBufferList()
1368 return LyX::ref().bufferList();
1372 LyXFunc & theLyXFunc()
1374 return LyX::ref().lyxFunc();
1378 LyXServer & theLyXServer()
1380 // FIXME: this should not be use_gui dependent
1381 BOOST_ASSERT(use_gui);
1382 return LyX::ref().server();
1386 LyXServerSocket & theLyXServerSocket()
1388 // FIXME: this should not be use_gui dependent
1389 BOOST_ASSERT(use_gui);
1390 return LyX::ref().socket();
1394 kb_keymap & theTopLevelKeymap()
1396 BOOST_ASSERT(use_gui);
1397 return LyX::ref().topLevelKeymap();
1401 IconvProcessor & utf8ToUcs4()
1403 return LyX::ref().iconvProcessor();