3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
20 #include "ConverterCache.h"
22 #include "buffer_funcs.h"
23 #include "bufferlist.h"
24 #include "converter.h"
27 #include "errorlist.h"
35 #include "LyXAction.h"
39 #include "lyxserver.h"
40 #include "lyxsocket.h"
41 #include "lyxtextclasslist.h"
42 #include "MenuBackend.h"
44 #include "ToolbarBackend.h"
46 #include "frontends/Alert.h"
47 #include "frontends/Application.h"
48 #include "frontends/Gui.h"
49 #include "frontends/LyXView.h"
51 #include "support/environment.h"
52 #include "support/filetools.h"
53 #include "support/fontutils.h"
54 #include "support/lyxlib.h"
55 #include "support/convert.h"
56 #include "support/os.h"
57 #include "support/package.h"
58 #include "support/path.h"
59 #include "support/systemcall.h"
60 #include "support/unicode.h"
62 #include <boost/bind.hpp>
63 #include <boost/filesystem/operations.hpp>
71 using support::addName;
72 using support::addPath;
73 using support::bformat;
74 using support::createDirectory;
75 using support::createLyXTmpDir;
76 using support::destroyDir;
77 using support::FileName;
78 using support::fileSearch;
79 using support::getEnv;
80 using support::i18nLibFileSearch;
81 using support::libFileSearch;
82 using support::package;
83 using support::prependEnvPath;
85 using support::Systemcall;
87 namespace Alert = frontend::Alert;
88 namespace os = support::os;
89 namespace fs = boost::filesystem;
96 #ifndef CXX_GLOBAL_CSTD
103 frontend::Application * theApp = 0;
105 /// are we using the GUI at all?
107 * We default to true and this is changed to false when the export feature is used.
114 // Filled with the command line arguments "foo" of "-sysdir foo" or
116 string cl_system_support;
117 string cl_user_support;
119 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;
173 // Static data are not treated in the same way at all on the Mac (and
174 // the LyX singleton has static methods). This is the reason why the
175 // exit command on the Mac bypasses our dispatch machinery altogether.
176 // On Linux and Windows we won't pass a second time through quit()
177 // because quitting will already be set to true.
185 BOOST_ASSERT(singleton_);
190 LyX const & LyX::cref()
192 BOOST_ASSERT(singleton_);
201 pimpl_.reset(new Singletons);
206 BufferList & LyX::bufferList()
208 return pimpl_->buffer_list_;
212 BufferList const & LyX::bufferList() const
214 return pimpl_->buffer_list_;
218 Session & LyX::session()
220 BOOST_ASSERT(pimpl_->session_.get());
221 return *pimpl_->session_.get();
225 Session const & LyX::session() const
227 BOOST_ASSERT(pimpl_->session_.get());
228 return *pimpl_->session_.get();
232 LyXFunc & LyX::lyxFunc()
234 return pimpl_->lyxfunc_;
238 LyXFunc const & LyX::lyxFunc() const
240 return pimpl_->lyxfunc_;
244 LyXServer & LyX::server()
246 BOOST_ASSERT(pimpl_->lyx_server_.get());
247 return *pimpl_->lyx_server_.get();
251 LyXServer const & LyX::server() const
253 BOOST_ASSERT(pimpl_->lyx_server_.get());
254 return *pimpl_->lyx_server_.get();
258 LyXServerSocket & LyX::socket()
260 BOOST_ASSERT(pimpl_->lyx_socket_.get());
261 return *pimpl_->lyx_socket_.get();
265 LyXServerSocket const & LyX::socket() const
267 BOOST_ASSERT(pimpl_->lyx_socket_.get());
268 return *pimpl_->lyx_socket_.get();
272 frontend::Application & LyX::application()
274 BOOST_ASSERT(pimpl_->application_.get());
275 return *pimpl_->application_.get();
279 frontend::Application const & LyX::application() const
281 BOOST_ASSERT(pimpl_->application_.get());
282 return *pimpl_->application_.get();
286 kb_keymap & LyX::topLevelKeymap()
288 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
289 return *pimpl_->toplevel_keymap_.get();
293 IconvProcessor & LyX::iconvProcessor()
295 return pimpl_->iconv;
299 kb_keymap const & LyX::topLevelKeymap() const
301 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
302 return *pimpl_->toplevel_keymap_.get();
306 Buffer const * const LyX::updateInset(InsetBase const * inset) const
311 Buffer const * buffer_ptr = 0;
312 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
313 vector<int>::const_iterator it = view_ids.begin();
314 vector<int>::const_iterator const end = view_ids.end();
315 for (; it != end; ++it) {
317 pimpl_->application_->gui().view(*it).updateInset(inset);
325 int LyX::exec(int & argc, char * argv[])
327 // Here we need to parse the command line. At least
328 // we need to parse for "-dbg" and "-help"
329 easyParse(argc, argv);
331 support::init_package(argv[0], cl_system_support, cl_user_support,
332 support::top_build_dir_is_one_level_up);
334 vector<string> files;
337 // FIXME: create a ConsoleApplication
338 int exit_status = loadFiles(argc, argv, files);
344 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
349 BufferList::iterator begin = pimpl_->buffer_list_.begin();
350 BufferList::iterator end = pimpl_->buffer_list_.end();
352 bool final_success = false;
353 for (BufferList::iterator I = begin; I != end; ++I) {
355 bool success = false;
356 buf->dispatch(batch_command, &success);
357 final_success |= success;
360 return !final_success;
363 // Force adding of font path _before_ Application is initialized
364 support::addFontResources();
366 // Let the frontend parse and remove all arguments that it knows
367 pimpl_->application_.reset(createApplication(argc, argv));
368 // FIXME: this global pointer should probably go.
369 theApp = pimpl_->application_.get();
373 // Parse and remove all known arguments in the LyX singleton
374 // Give an error for all remaining ones.
375 int exit_status = loadFiles(argc, argv, files);
377 // Kill the application object before exiting.
378 pimpl_->application_.reset();
384 restoreGuiSession(files);
385 // Start the real execution loop.
388 /* Create a CoreApplication class that will provide the main event loop
389 * and the socket callback registering. With Qt4, only QtCore
390 * library would be needed.
391 * When this is done, a server_mode could be created and the following two
392 * line would be moved out from here.
394 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
395 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
396 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
398 exit_status = pimpl_->application_->exec();
400 // FIXME: Do we still need this reset?
401 // I assume it is the reason for strange Mac crashs
402 // Test by reverting rev 16110 (Peter)
403 // Kill the application object before exiting. This avoid crash
405 pimpl_->application_.reset();
407 // Restore original font resources after Application is destroyed.
408 support::restoreFontResources();
414 void LyX::prepareExit()
416 // Set a flag that we do quitting from the program,
417 // so no refreshes are necessary.
420 // close buffers first
421 pimpl_->buffer_list_.closeAll();
423 // do any other cleanup procedures now
424 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
426 // Prevent the deletion of /tmp if LyX was called with invalid
427 // arguments. Does not work on windows.
428 // FIXME: Fix the real bug instead.
429 if (package().temp_dir() == "/tmp") {
430 lyxerr << "Not deleting /tmp." << endl;
434 if (!destroyDir(package().temp_dir())) {
435 docstring const msg =
436 bformat(_("Unable to remove the temporary directory %1$s"),
437 from_utf8(package().temp_dir()));
438 Alert::warning(_("Unable to remove temporary directory"), msg);
443 void LyX::earlyExit(int status)
445 BOOST_ASSERT(pimpl_->application_.get());
446 // LyX::pimpl_::application_ is not initialised at this
447 // point so it's safe to just exit after some cleanup.
455 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
459 if (pimpl_->session_)
460 pimpl_->session_->writeFile();
461 pimpl_->lyx_server_.reset();
462 pimpl_->lyx_socket_.reset();
463 if (pimpl_->application_)
464 pimpl_->application_->exit(0);
470 int LyX::loadFiles(int & argc, char * argv[],
471 vector<string> & files)
473 // check for any spurious extra arguments
474 // other than documents
475 for (int argi = 1; argi < argc ; ++argi) {
476 if (argv[argi][0] == '-') {
478 bformat(_("Wrong command line option `%1$s'. Exiting."),
479 from_utf8(argv[argi]))) << endl;
484 // Initialization of LyX (reads lyxrc and more)
485 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
486 bool success = init();
487 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
491 for (int argi = argc - 1; argi >= 1; --argi) {
492 // check for any remaining extra arguments other than
493 // document file names. These will be passed out to the
495 if (argv[argi][0] == '-')
497 files.push_back(os::internal_path(argv[argi]));
501 files.push_back(i18nLibFileSearch("examples", "splash.lyx").absFilename());
503 Buffer * last_loaded = 0;
505 vector<string>::const_iterator it = files.begin();
506 vector<string>::const_iterator end = files.end();
508 for (; it != end; ++it) {
509 // get absolute path of file and add ".lyx" to
510 // the filename if necessary
511 string s = fileSearch(string(), *it, "lyx").absFilename();
513 Buffer * const b = newFile(*it, string(), true);
517 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
518 if (loadLyXFile(buf, s)) {
520 ErrorList const & el = buf->errorList("Parse");
522 for_each(el.begin(), el.end(),
523 boost::bind(&LyX::printError, this, _1));
526 pimpl_->buffer_list_.release(buf);
530 files.clear(); // the files are already loaded
536 void LyX::execBatchCommands()
538 // Execute batch commands if available
539 if (batch_command.empty())
542 lyxerr[Debug::INIT] << "About to handle -x '"
543 << batch_command << '\'' << endl;
545 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
549 void LyX::restoreGuiSession(vector<string> const & files)
551 LyXView * view = newLyXView();
554 for_each(files.begin(), files.end(),
555 bind(&LyXView::loadLyXFile, view, _1, true));
557 // if a file is specified, I assume that user wants to edit *that* file
558 if (files.empty() && lyxrc.load_session) {
559 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
560 // do not add to the lastfile list since these files are restored from
561 // last seesion, and should be already there (regular files), or should
562 // not be added at all (help files).
563 for_each(lastopened.begin(), lastopened.end(),
564 bind(&LyXView::loadLyXFile, view, _1, false));
566 // clear this list to save a few bytes of RAM
567 pimpl_->session_->lastOpened().clear();
571 LyXView * LyX::newLyXView()
576 // determine windows size and position, from lyxrc and/or session
578 unsigned int width = 690;
579 unsigned int height = 510;
580 // default icon size, will be overwritten by stored session value
581 unsigned int iconSizeXY = 0;
582 bool maximize = false;
584 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
585 width = lyxrc.geometry_width;
586 height = lyxrc.geometry_height;
588 // if lyxrc returns (0,0), then use session info
590 string val = session().sessionInfo().load("WindowWidth");
592 width = convert<unsigned int>(val);
593 val = session().sessionInfo().load("WindowHeight");
595 height = convert<unsigned int>(val);
596 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
598 val = session().sessionInfo().load("IconSizeXY");
600 iconSizeXY = convert<unsigned int>(val);
603 // if user wants to restore window position
606 if (lyxrc.geometry_xysaved) {
607 string val = session().sessionInfo().load("WindowPosX");
609 posx = convert<int>(val);
610 val = session().sessionInfo().load("WindowPosY");
612 posy = convert<int>(val);
615 if (!geometryArg.empty())
621 // create the main window
622 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
630 The SIGHUP signal does not exist on Windows and does not need to be handled.
632 Windows handles SIGFPE and SIGSEGV signals as expected.
634 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
635 cause a new thread to be spawned. This may well result in unexpected
636 behaviour by the single-threaded LyX.
638 SIGTERM signals will come only from another process actually sending
639 that signal using 'raise' in Windows' POSIX compatability layer. It will
640 not come from the general "terminate process" methods that everyone
641 actually uses (and which can't be trapped). Killing an app 'politely' on
642 Windows involves first sending a WM_CLOSE message, something that is
643 caught already by the Qt frontend.
645 For more information see:
647 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
648 ...signals are mostly useless on Windows for a variety of reasons that are
651 'UNIX Application Migration Guide, Chapter 9'
652 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
654 'How To Terminate an Application "Cleanly" in Win32'
655 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
659 static void error_handler(int err_sig)
661 // Throw away any signals other than the first one received.
662 static sig_atomic_t handling_error = false;
665 handling_error = true;
667 // We have received a signal indicating a fatal error, so
668 // try and save the data ASAP.
669 LyX::cref().emergencyCleanup();
671 // These lyxerr calls may or may not work:
673 // Signals are asynchronous, so the main program may be in a very
674 // fragile state when a signal is processed and thus while a signal
675 // handler function executes.
676 // In general, therefore, we should avoid performing any
677 // I/O operations or calling most library and system functions from
680 // This shouldn't matter here, however, as we've already invoked
685 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
689 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
692 lyxerr << "\nlyx: SIGSEGV signal caught\n"
693 "Sorry, you have found a bug in LyX. "
694 "Please read the bug-reporting instructions "
695 "in Help->Introduction and send us a bug report, "
696 "if necessary. Thanks !\nBye." << endl;
704 // Deinstall the signal handlers
706 signal(SIGHUP, SIG_DFL);
708 signal(SIGINT, SIG_DFL);
709 signal(SIGFPE, SIG_DFL);
710 signal(SIGSEGV, SIG_DFL);
711 signal(SIGTERM, SIG_DFL);
714 if (err_sig == SIGSEGV ||
715 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
717 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
726 void LyX::printError(ErrorItem const & ei)
728 docstring tmp = _("LyX: ") + ei.error + char_type(':')
730 std::cerr << to_utf8(tmp) << std::endl;
734 void LyX::initGuiFont()
736 if (lyxrc.roman_font_name.empty())
737 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
739 if (lyxrc.sans_font_name.empty())
740 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
742 if (lyxrc.typewriter_font_name.empty())
743 lyxrc.typewriter_font_name
744 = pimpl_->application_->typewriterFontName();
751 signal(SIGHUP, error_handler);
753 signal(SIGFPE, error_handler);
754 signal(SIGSEGV, error_handler);
755 signal(SIGINT, error_handler);
756 signal(SIGTERM, error_handler);
757 // SIGPIPE can be safely ignored.
759 lyxrc.tempdir_path = package().temp_dir();
760 lyxrc.document_path = package().document_dir();
762 if (lyxrc.template_path.empty()) {
763 lyxrc.template_path = addPath(package().system_support(),
768 // Read configuration files
771 // This one may have been distributed along with LyX.
772 if (!readRcFile("lyxrc.dist"))
775 // Set the PATH correctly.
776 #if !defined (USE_POSIX_PACKAGING)
777 // Add the directory containing the LyX executable to the path
778 // so that LyX can find things like tex2lyx.
779 if (package().build_support().empty())
780 prependEnvPath("PATH", package().binary_dir());
782 if (!lyxrc.path_prefix.empty())
783 prependEnvPath("PATH", lyxrc.path_prefix);
785 // Check that user LyX directory is ok.
786 if (queryUserLyXDir(package().explicit_user_support()))
787 reconfigureUserLyXDir();
789 // no need for a splash when there is no GUI
794 // This one is generated in user_support directory by lib/configure.py.
795 if (!readRcFile("lyxrc.defaults"))
798 // Query the OS to know what formats are viewed natively
799 formats.setAutoOpen();
801 // Read lyxrc.dist again to be able to override viewer auto-detection.
802 readRcFile("lyxrc.dist");
804 system_lyxrc = lyxrc;
805 system_formats = formats;
806 system_converters = converters;
807 system_movers = movers;
808 system_lcolor = lcolor;
810 // This one is edited through the preferences dialog.
811 if (!readRcFile("preferences"))
814 if (!readEncodingsFile("encodings"))
816 if (!readLanguagesFile("languages"))
820 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
826 pimpl_->toplevel_keymap_.reset(new kb_keymap);
827 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
828 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
830 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
833 if (!readUIFile(lyxrc.ui_file))
837 if (lyxerr.debugging(Debug::LYXRC))
840 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
841 if (!lyxrc.path_prefix.empty())
842 prependEnvPath("PATH", lyxrc.path_prefix);
844 if (fs::exists(lyxrc.document_path) &&
845 fs::is_directory(lyxrc.document_path))
846 package().document_dir() = lyxrc.document_path;
848 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
849 if (package().temp_dir().empty()) {
850 Alert::error(_("Could not create temporary directory"),
851 bformat(_("Could not create a temporary directory in\n"
852 "%1$s. Make sure that this\n"
853 "path exists and is writable and try again."),
854 from_utf8(lyxrc.tempdir_path)));
855 // createLyXTmpDir() tries sufficiently hard to create a
856 // usable temp dir, so the probability to come here is
857 // close to zero. We therefore don't try to overcome this
858 // problem with e.g. asking the user for a new path and
859 // trying again but simply exit.
863 if (lyxerr.debugging(Debug::INIT)) {
864 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
867 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
868 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
870 // This must happen after package initialization and after lyxrc is
871 // read, therefore it can't be done by a static object.
872 ConverterCache::init();
878 void LyX::defaultKeyBindings(kb_keymap * kbmap)
880 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
881 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
882 kbmap->bind("Up", FuncRequest(LFUN_UP));
883 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
885 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
886 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
887 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
888 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
890 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
891 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
892 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
893 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
895 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
896 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
898 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
899 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
901 // kbmap->bindings to enable the use of the numeric keypad
903 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
904 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
905 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
906 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
915 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
916 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
917 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
918 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
919 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
920 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
921 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
922 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
923 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
924 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
925 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
926 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
930 void LyX::emergencyCleanup() const
932 // what to do about tmpfiles is non-obvious. we would
933 // like to delete any we find, but our lyxdir might
934 // contain documents etc. which might be helpful on
937 pimpl_->buffer_list_.emergencyWriteAll();
939 if (pimpl_->lyx_server_)
940 pimpl_->lyx_server_->emergencyCleanup();
941 pimpl_->lyx_server_.reset();
942 pimpl_->lyx_socket_.reset();
947 void LyX::deadKeyBindings(kb_keymap * kbmap)
949 // bindKeyings for transparent handling of deadkeys
950 // The keysyms are gotten from XFree86 X11R6
951 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
952 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
953 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
954 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
955 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
956 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
957 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
958 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
959 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
960 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
961 // nothing with this name
962 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
963 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
964 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
965 // nothing with this name either...
966 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
967 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
968 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
969 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
975 // return true if file does not exist or is older than configure.py.
976 bool needsUpdate(string const & file)
978 static string const configure_script =
979 addName(package().system_support(), "configure.py");
980 string const absfile =
981 addName(package().user_support(), file);
983 return (! fs::exists(absfile))
984 || (fs::last_write_time(configure_script)
985 > fs::last_write_time(absfile));
991 bool LyX::queryUserLyXDir(bool explicit_userdir)
993 // Does user directory exist?
994 if (fs::exists(package().user_support()) &&
995 fs::is_directory(package().user_support())) {
998 return needsUpdate("lyxrc.defaults")
999 || needsUpdate("textclass.lst")
1000 || needsUpdate("packages.lst");
1003 first_start = !explicit_userdir;
1005 // If the user specified explicitly a directory, ask whether
1006 // to create it. If the user says "no", then exit.
1007 if (explicit_userdir &&
1009 _("Missing user LyX directory"),
1010 bformat(_("You have specified a non-existent user "
1011 "LyX directory, %1$s.\n"
1012 "It is needed to keep your own configuration."),
1013 from_utf8(package().user_support())),
1015 _("&Create directory"),
1017 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1018 earlyExit(EXIT_FAILURE);
1021 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1022 from_utf8(package().user_support())))
1025 if (!createDirectory(package().user_support(), 0755)) {
1026 // Failed, so let's exit.
1027 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1029 earlyExit(EXIT_FAILURE);
1036 bool LyX::readRcFile(string const & name)
1038 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1040 FileName const lyxrc_path = libFileSearch(string(), name);
1041 if (!lyxrc_path.empty()) {
1043 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1045 if (lyxrc.read(lyxrc_path) < 0) {
1046 showFileError(name);
1050 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1056 // Read the ui file `name'
1057 bool LyX::readUIFile(string const & name)
1067 struct keyword_item uitags[ui_last - 1] = {
1068 { "include", ui_include },
1069 { "menuset", ui_menuset },
1070 { "toolbar", ui_toolbar },
1071 { "toolbars", ui_toolbars }
1074 // Ensure that a file is read only once (prevents include loops)
1075 static std::list<string> uifiles;
1076 std::list<string>::const_iterator it = uifiles.begin();
1077 std::list<string>::const_iterator end = uifiles.end();
1078 it = std::find(it, end, name);
1080 lyxerr[Debug::INIT] << "UI file '" << name
1081 << "' has been read already. "
1082 << "Is this an include loop?"
1087 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1089 FileName const ui_path = libFileSearch("ui", name, "ui");
1091 if (ui_path.empty()) {
1092 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1093 showFileError(name);
1096 uifiles.push_back(name);
1098 lyxerr[Debug::INIT] << "Found " << name
1099 << " in " << ui_path << endl;
1100 LyXLex lex(uitags, ui_last - 1);
1101 lex.setFile(ui_path);
1103 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1107 if (lyxerr.debugging(Debug::PARSER))
1108 lex.printTable(lyxerr);
1110 while (lex.isOK()) {
1111 switch (lex.lex()) {
1114 string const file = lex.getString();
1115 if (!readUIFile(file))
1120 menubackend.read(lex);
1124 toolbarbackend.read(lex);
1128 toolbarbackend.readToolbars(lex);
1132 if (!rtrim(lex.getString()).empty())
1133 lex.printError("LyX::ReadUIFile: "
1134 "Unknown menu tag: `$$Token'");
1142 // Read the languages file `name'
1143 bool LyX::readLanguagesFile(string const & name)
1145 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1147 FileName const lang_path = libFileSearch(string(), name);
1148 if (lang_path.empty()) {
1149 showFileError(name);
1152 languages.read(lang_path);
1157 // Read the encodings file `name'
1158 bool LyX::readEncodingsFile(string const & name)
1160 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1162 FileName const enc_path = libFileSearch(string(), name);
1163 if (enc_path.empty()) {
1164 showFileError(name);
1167 encodings.read(enc_path);
1176 /// return the the number of arguments consumed
1177 typedef boost::function<int(string const &, string const &)> cmd_helper;
1179 int parse_dbg(string const & arg, string const &)
1182 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1183 Debug::showTags(lyxerr);
1186 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1188 lyxerr.level(Debug::value(arg));
1189 Debug::showLevel(lyxerr, lyxerr.level());
1194 int parse_help(string const &, string const &)
1197 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1198 "Command line switches (case sensitive):\n"
1199 "\t-help summarize LyX usage\n"
1200 "\t-userdir dir set user directory to dir\n"
1201 "\t-sysdir dir set system directory to dir\n"
1202 "\t-geometry WxH+X+Y set geometry of the main window\n"
1203 "\t-dbg feature[,feature]...\n"
1204 " select the features to debug.\n"
1205 " Type `lyx -dbg' to see the list of features\n"
1206 "\t-x [--execute] command\n"
1207 " where command is a lyx command.\n"
1208 "\t-e [--export] fmt\n"
1209 " where fmt is the export format of choice.\n"
1210 "\t-i [--import] fmt file.xxx\n"
1211 " where fmt is the import format of choice\n"
1212 " and file.xxx is the file to be imported.\n"
1213 "\t-version summarize version and build info\n"
1214 "Check the LyX man page for more details.")) << endl;
1219 int parse_version(string const &, string const &)
1221 lyxerr << "LyX " << lyx_version
1222 << " (" << lyx_release_date << ")" << endl;
1223 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1225 lyxerr << lyx_version_info << endl;
1230 int parse_sysdir(string const & arg, string const &)
1233 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1236 cl_system_support = arg;
1240 int parse_userdir(string const & arg, string const &)
1243 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1246 cl_user_support = arg;
1250 int parse_execute(string const & arg, string const &)
1253 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1260 int parse_export(string const & type, string const &)
1263 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1264 "--export switch")) << endl;
1267 batch = "buffer-export " + type;
1272 int parse_import(string const & type, string const & file)
1275 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1276 "--import switch")) << endl;
1280 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1284 batch = "buffer-import " + type + ' ' + file;
1288 int parse_geometry(string const & arg1, string const &)
1291 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1292 // remove also the arg
1295 // don't remove "-geometry"
1304 void LyX::easyParse(int & argc, char * argv[])
1306 std::map<string, cmd_helper> cmdmap;
1308 cmdmap["-dbg"] = parse_dbg;
1309 cmdmap["-help"] = parse_help;
1310 cmdmap["--help"] = parse_help;
1311 cmdmap["-version"] = parse_version;
1312 cmdmap["--version"] = parse_version;
1313 cmdmap["-sysdir"] = parse_sysdir;
1314 cmdmap["-userdir"] = parse_userdir;
1315 cmdmap["-x"] = parse_execute;
1316 cmdmap["--execute"] = parse_execute;
1317 cmdmap["-e"] = parse_export;
1318 cmdmap["--export"] = parse_export;
1319 cmdmap["-i"] = parse_import;
1320 cmdmap["--import"] = parse_import;
1321 cmdmap["-geometry"] = parse_geometry;
1323 for (int i = 1; i < argc; ++i) {
1324 std::map<string, cmd_helper>::const_iterator it
1325 = cmdmap.find(argv[i]);
1327 // don't complain if not found - may be parsed later
1328 if (it == cmdmap.end())
1331 string arg((i + 1 < argc) ? argv[i + 1] : "");
1332 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1334 int const remove = 1 + it->second(arg, arg2);
1336 // Now, remove used arguments by shifting
1337 // the following ones remove places down.
1340 for (int j = i; j < argc; ++j)
1341 argv[j] = argv[j + remove];
1346 batch_command = batch;
1350 FuncStatus getStatus(FuncRequest const & action)
1352 return LyX::ref().lyxFunc().getStatus(action);
1356 void dispatch(FuncRequest const & action)
1358 LyX::ref().lyxFunc().dispatch(action);
1362 BufferList & theBufferList()
1364 return LyX::ref().bufferList();
1368 LyXFunc & theLyXFunc()
1370 return LyX::ref().lyxFunc();
1374 LyXServer & theLyXServer()
1376 // FIXME: this should not be use_gui dependent
1377 BOOST_ASSERT(use_gui);
1378 return LyX::ref().server();
1382 LyXServerSocket & theLyXServerSocket()
1384 // FIXME: this should not be use_gui dependent
1385 BOOST_ASSERT(use_gui);
1386 return LyX::ref().socket();
1390 kb_keymap & theTopLevelKeymap()
1392 BOOST_ASSERT(use_gui);
1393 return LyX::ref().topLevelKeymap();
1397 IconvProcessor & utf8ToUcs4()
1399 return LyX::ref().iconvProcessor();