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.
21 #include "buffer_funcs.h"
22 #include "bufferlist.h"
23 #include "converter.h"
26 #include "errorlist.h"
34 #include "LyXAction.h"
38 #include "lyxserver.h"
39 #include "lyxsocket.h"
40 #include "lyxtextclasslist.h"
41 #include "MenuBackend.h"
43 #include "ToolbarBackend.h"
45 #include "frontends/Alert.h"
46 #include "frontends/Application.h"
47 #include "frontends/LyXView.h"
49 #include "support/environment.h"
50 #include "support/filetools.h"
51 #include "support/fontutils.h"
52 #include "support/lyxlib.h"
53 #include "support/convert.h"
54 #include "support/os.h"
55 #include "support/package.h"
56 #include "support/path.h"
57 #include "support/systemcall.h"
59 #include <boost/bind.hpp>
60 #include <boost/filesystem/operations.hpp>
68 using support::addName;
69 using support::addPath;
70 using support::bformat;
71 using support::createDirectory;
72 using support::createLyXTmpDir;
73 using support::destroyDir;
74 using support::fileSearch;
75 using support::getEnv;
76 using support::i18nLibFileSearch;
77 using support::libFileSearch;
78 using support::package;
79 using support::prependEnvPath;
81 using support::Systemcall;
83 namespace Alert = frontend::Alert;
84 namespace os = support::os;
85 namespace fs = boost::filesystem;
92 #ifndef CXX_GLOBAL_CSTD
99 frontend::Application * theApp = 0;
101 /// are we using the GUI at all?
103 * We default to true and this is changed to false when the export feature is used.
110 // Filled with the command line arguments "foo" of "-sysdir foo" or
112 string cl_system_support;
113 string cl_user_support;
116 void showFileError(string const & error)
118 Alert::warning(_("Could not read configuration file"),
119 bformat(_("Error while reading the configuration file\n%1$s.\n"
120 "Please check your installation."), from_utf8(error)));
124 void reconfigureUserLyXDir()
126 string const configure_command = package().configure_command();
128 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
129 support::Path p(package().user_support());
131 one.startscript(Systemcall::Wait, configure_command);
132 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
138 /// The main application class private implementation.
139 struct LyX::Singletons
141 /// our function handler
144 BufferList buffer_list_;
146 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
148 boost::scoped_ptr<LyXServer> lyx_server_;
150 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
152 boost::scoped_ptr<frontend::Application> application_;
153 /// lyx session, containing lastfiles, lastfilepos, and lastopened
154 boost::scoped_ptr<Session> session_;
158 boost::scoped_ptr<LyX> LyX::singleton_;
161 int LyX::exec(int & argc, char * argv[])
163 BOOST_ASSERT(!singleton_.get());
164 // We must return from this before launching the gui so that
165 // other parts of the code can access singleton_ through
166 // LyX::ref and LyX::cref.
167 singleton_.reset(new LyX);
168 // Start the real execution loop.
169 return singleton_->priv_exec(argc, argv);
175 BOOST_ASSERT(singleton_.get());
176 return *singleton_.get();
180 LyX const & LyX::cref()
182 BOOST_ASSERT(singleton_.get());
183 return *singleton_.get();
188 : first_start(false), geometryOption_(false)
190 pimpl_.reset(new Singletons);
194 BufferList & LyX::bufferList()
196 return pimpl_->buffer_list_;
200 BufferList const & LyX::bufferList() const
202 return pimpl_->buffer_list_;
206 Session & LyX::session()
208 BOOST_ASSERT(pimpl_->session_.get());
209 return *pimpl_->session_.get();
213 Session const & LyX::session() const
215 BOOST_ASSERT(pimpl_->session_.get());
216 return *pimpl_->session_.get();
220 LyXFunc & LyX::lyxFunc()
222 return pimpl_->lyxfunc_;
226 LyXFunc const & LyX::lyxFunc() const
228 return pimpl_->lyxfunc_;
232 LyXServer & LyX::server()
234 BOOST_ASSERT(pimpl_->lyx_server_.get());
235 return *pimpl_->lyx_server_.get();
239 LyXServer const & LyX::server() const
241 BOOST_ASSERT(pimpl_->lyx_server_.get());
242 return *pimpl_->lyx_server_.get();
246 LyXServerSocket & LyX::socket()
248 BOOST_ASSERT(pimpl_->lyx_socket_.get());
249 return *pimpl_->lyx_socket_.get();
253 LyXServerSocket const & LyX::socket() const
255 BOOST_ASSERT(pimpl_->lyx_socket_.get());
256 return *pimpl_->lyx_socket_.get();
260 frontend::Application & LyX::application()
262 BOOST_ASSERT(pimpl_->application_.get());
263 return *pimpl_->application_.get();
267 frontend::Application const & LyX::application() const
269 BOOST_ASSERT(pimpl_->application_.get());
270 return *pimpl_->application_.get();
274 kb_keymap & LyX::topLevelKeymap()
276 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
277 return *pimpl_->toplevel_keymap_.get();
281 kb_keymap const & LyX::topLevelKeymap() const
283 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
284 return *pimpl_->toplevel_keymap_.get();
287 void LyX::addLyXView(LyXView * lyxview)
289 views_.push_back(lyxview);
293 Buffer const * const LyX::updateInset(InsetBase const * inset) const
298 Buffer const * buffer_ptr = 0;
299 ViewList::const_iterator it = views_.begin();
300 ViewList::const_iterator const end = views_.end();
301 for (; it != end; ++it) {
302 Buffer const * ptr = (*it)->updateInset(inset);
310 int LyX::priv_exec(int & argc, char * argv[])
312 // Here we need to parse the command line. At least
313 // we need to parse for "-dbg" and "-help"
314 easyParse(argc, argv);
316 support::init_package(argv[0], cl_system_support, cl_user_support,
317 support::top_build_dir_is_one_level_up);
319 vector<string> files;
320 int exit_status = execBatchCommands(argc, argv, files);
326 // Force adding of font path _before_ Application is initialized
327 support::addFontResources();
328 pimpl_->application_.reset(createApplication(argc, argv));
330 // FIXME: this global pointer should probably go.
331 theApp = pimpl_->application_.get();
332 restoreGuiSession(files);
333 // Start the real execution loop.
336 /* Create a CoreApplication class that will provide the main event loop and
337 * the socket callback registering. With Qt4, only QtCore library would be needed.
338 * When this done, a server_mode could be created and the following two
339 * line would be moved out from here.
341 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
342 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
343 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
345 // handle the batch commands the user asked for
346 if (!batch_command.empty()) {
347 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
350 exit_status = pimpl_->application_->start(batch_command);
351 // Kill the application object before exiting. This avoid crash
353 pimpl_->application_.reset();
354 // Restore original font resources after Application is destroyed.
355 support::restoreFontResources();
358 // FIXME: create a ConsoleApplication
366 void LyX::prepareExit()
368 // Set a flag that we do quitting from the program,
369 // so no refreshes are necessary.
372 // close buffers first
373 pimpl_->buffer_list_.closeAll();
375 // do any other cleanup procedures now
376 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
378 if (!destroyDir(package().temp_dir())) {
379 docstring const msg =
380 bformat(_("Unable to remove the temporary directory %1$s"),
381 from_utf8(package().temp_dir()));
382 Alert::warning(_("Unable to remove temporary directory"), msg);
387 void LyX::earlyExit(int status)
389 BOOST_ASSERT(pimpl_->application_.get());
390 // LyX::pimpl_::application_ is not initialised at this
391 // point so it's safe to just exit after some cleanup.
397 void LyX::quit(bool noask)
399 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
402 if (!noask && !pimpl_->buffer_list_.quitWriteAll())
405 pimpl_->session_->writeFile();
411 pimpl_->lyx_server_.reset();
412 pimpl_->lyx_socket_.reset();
413 pimpl_->application_->exit(0);
418 int LyX::execBatchCommands(int & argc, char * argv[],
419 vector<string> & files)
421 // check for any spurious extra arguments
422 // other than documents
423 for (int argi = 1; argi < argc ; ++argi) {
424 if (argv[argi][0] == '-') {
426 bformat(_("Wrong command line option `%1$s'. Exiting."),
427 from_utf8(argv[argi]))) << endl;
432 // Initialization of LyX (reads lyxrc and more)
433 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
434 bool success = init();
435 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
439 for (int argi = argc - 1; argi >= 1; --argi)
440 files.push_back(os::internal_path(argv[argi]));
443 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
445 // Execute batch commands if available
446 if (!batch_command.empty()) {
448 lyxerr[Debug::INIT] << "About to handle -x '"
449 << batch_command << '\'' << endl;
451 Buffer * last_loaded = 0;
453 vector<string>::const_iterator it = files.begin();
454 vector<string>::const_iterator end = files.end();
456 for (; it != end; ++it) {
457 // get absolute path of file and add ".lyx" to
458 // the filename if necessary
459 string s = fileSearch(string(), *it, "lyx");
461 Buffer * const b = newFile(*it, string(), true);
465 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
466 if (loadLyXFile(buf, s)) {
468 ErrorList const & el = buf->errorList("Parse");
470 for_each(el.begin(), el.end(),
471 boost::bind(&LyX::printError, this, _1));
474 pimpl_->buffer_list_.release(buf);
478 // try to dispatch to last loaded buffer first
481 if (last_loaded->dispatch(batch_command, &success)) {
486 files.clear(); // the files are already loaded
493 void LyX::restoreGuiSession(vector<string> const & files)
495 LyXView * view = newLyXView();
498 for_each(files.begin(), files.end(),
499 bind(&LyXView::loadLyXFile, view, _1, true));
501 // if a file is specified, I assume that user wants to edit *that* file
502 if (files.empty() && lyxrc.load_session) {
503 vector<string> const & lastopened = pimpl_->session_->lastOpenedFiles();
504 // do not add to the lastfile list since these files are restored from
505 // last seesion, and should be already there (regular files), or should
506 // not be added at all (help files).
507 for_each(lastopened.begin(), lastopened.end(),
508 bind(&LyXView::loadLyXFile, view, _1, false));
510 // clear this list to save a few bytes of RAM
511 pimpl_->session_->clearLastOpenedFiles();
515 LyXView * LyX::newLyXView()
517 // determine windows size and position, from lyxrc and/or session
519 unsigned int width = 690;
520 unsigned int height = 510;
521 bool maximize = false;
523 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
524 width = lyxrc.geometry_width;
525 height = lyxrc.geometry_height;
527 // if lyxrc returns (0,0), then use session info
529 string val = session().loadSessionInfo("WindowWidth");
531 width = convert<unsigned int>(val);
532 val = session().loadSessionInfo("WindowHeight");
534 height = convert<unsigned int>(val);
535 if (session().loadSessionInfo("WindowIsMaximized") == "yes")
539 // if user wants to restore window position
542 if (lyxrc.geometry_xysaved) {
543 string val = session().loadSessionInfo("WindowPosX");
545 posx = convert<int>(val);
546 val = session().loadSessionInfo("WindowPosY");
548 posy = convert<int>(val);
551 if (geometryOption_) {
555 // create the main window
556 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
557 ref().addLyXView(view);
565 The SIGHUP signal does not exist on Windows and does not need to be handled.
567 Windows handles SIGFPE and SIGSEGV signals as expected.
569 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
570 cause a new thread to be spawned. This may well result in unexpected
571 behaviour by the single-threaded LyX.
573 SIGTERM signals will come only from another process actually sending
574 that signal using 'raise' in Windows' POSIX compatability layer. It will
575 not come from the general "terminate process" methods that everyone
576 actually uses (and which can't be trapped). Killing an app 'politely' on
577 Windows involves first sending a WM_CLOSE message, something that is
578 caught already by the Qt frontend.
580 For more information see:
582 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
583 ...signals are mostly useless on Windows for a variety of reasons that are
586 'UNIX Application Migration Guide, Chapter 9'
587 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
589 'How To Terminate an Application "Cleanly" in Win32'
590 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
594 static void error_handler(int err_sig)
596 // Throw away any signals other than the first one received.
597 static sig_atomic_t handling_error = false;
600 handling_error = true;
602 // We have received a signal indicating a fatal error, so
603 // try and save the data ASAP.
604 LyX::cref().emergencyCleanup();
606 // These lyxerr calls may or may not work:
608 // Signals are asynchronous, so the main program may be in a very
609 // fragile state when a signal is processed and thus while a signal
610 // handler function executes.
611 // In general, therefore, we should avoid performing any
612 // I/O operations or calling most library and system functions from
615 // This shouldn't matter here, however, as we've already invoked
620 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
624 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
627 lyxerr << "\nlyx: SIGSEGV signal caught\n"
628 "Sorry, you have found a bug in LyX. "
629 "Please read the bug-reporting instructions "
630 "in Help->Introduction and send us a bug report, "
631 "if necessary. Thanks !\nBye." << endl;
639 // Deinstall the signal handlers
641 signal(SIGHUP, SIG_DFL);
643 signal(SIGINT, SIG_DFL);
644 signal(SIGFPE, SIG_DFL);
645 signal(SIGSEGV, SIG_DFL);
646 signal(SIGTERM, SIG_DFL);
649 if (err_sig == SIGSEGV ||
650 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
652 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
661 void LyX::printError(ErrorItem const & ei)
663 docstring tmp = _("LyX: ") + ei.error + char_type(':')
665 std::cerr << to_utf8(tmp) << std::endl;
669 void LyX::initGuiFont()
671 if (lyxrc.roman_font_name.empty())
672 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
674 if (lyxrc.sans_font_name.empty())
675 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
677 if (lyxrc.typewriter_font_name.empty())
678 lyxrc.typewriter_font_name
679 = pimpl_->application_->typewriterFontName();
686 signal(SIGHUP, error_handler);
688 signal(SIGFPE, error_handler);
689 signal(SIGSEGV, error_handler);
690 signal(SIGINT, error_handler);
691 signal(SIGTERM, error_handler);
692 // SIGPIPE can be safely ignored.
694 lyxrc.tempdir_path = package().temp_dir();
695 lyxrc.document_path = package().document_dir();
697 if (lyxrc.template_path.empty()) {
698 lyxrc.template_path = addPath(package().system_support(),
703 // Read configuration files
706 // This one may have been distributed along with LyX.
707 if (!readRcFile("lyxrc.dist"))
710 // Set the PATH correctly.
711 #if !defined (USE_POSIX_PACKAGING)
712 // Add the directory containing the LyX executable to the path
713 // so that LyX can find things like tex2lyx.
714 if (package().build_support().empty())
715 prependEnvPath("PATH", package().binary_dir());
717 if (!lyxrc.path_prefix.empty())
718 prependEnvPath("PATH", lyxrc.path_prefix);
720 // Check that user LyX directory is ok.
721 if (queryUserLyXDir(package().explicit_user_support()))
722 reconfigureUserLyXDir();
724 // no need for a splash when there is no GUI
729 // This one is generated in user_support directory by lib/configure.py.
730 if (!readRcFile("lyxrc.defaults"))
733 // Query the OS to know what formats are viewed natively
734 formats.setAutoOpen();
736 system_lyxrc = lyxrc;
737 system_formats = formats;
738 system_converters = converters;
739 system_movers = movers;
740 system_lcolor = lcolor;
742 // This one is edited through the preferences dialog.
743 if (!readRcFile("preferences"))
746 if (!readEncodingsFile("encodings"))
748 if (!readLanguagesFile("languages"))
752 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
758 pimpl_->toplevel_keymap_.reset(new kb_keymap);
759 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
760 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
762 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
765 if (!readUIFile(lyxrc.ui_file))
769 if (lyxerr.debugging(Debug::LYXRC))
772 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
773 if (!lyxrc.path_prefix.empty())
774 prependEnvPath("PATH", lyxrc.path_prefix);
776 if (fs::exists(lyxrc.document_path) &&
777 fs::is_directory(lyxrc.document_path))
778 package().document_dir() = lyxrc.document_path;
780 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
781 if (package().temp_dir().empty()) {
782 Alert::error(_("Could not create temporary directory"),
783 bformat(_("Could not create a temporary directory in\n"
784 "%1$s. Make sure that this\n"
785 "path exists and is writable and try again."),
786 from_utf8(lyxrc.tempdir_path)));
787 // createLyXTmpDir() tries sufficiently hard to create a
788 // usable temp dir, so the probability to come here is
789 // close to zero. We therefore don't try to overcome this
790 // problem with e.g. asking the user for a new path and
791 // trying again but simply exit.
795 if (lyxerr.debugging(Debug::INIT)) {
796 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
799 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
800 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
805 void LyX::defaultKeyBindings(kb_keymap * kbmap)
807 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
808 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
809 kbmap->bind("Up", FuncRequest(LFUN_UP));
810 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
812 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
813 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
814 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
815 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
817 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
818 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
819 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
820 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
822 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
823 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
825 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
826 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
828 // kbmap->bindings to enable the use of the numeric keypad
830 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
831 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
832 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
833 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
834 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
835 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
836 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
837 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
838 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
839 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
840 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
841 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
842 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
843 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
844 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
845 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
846 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
847 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
848 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
849 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
850 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
851 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
852 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
853 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
857 void LyX::emergencyCleanup() const
859 // what to do about tmpfiles is non-obvious. we would
860 // like to delete any we find, but our lyxdir might
861 // contain documents etc. which might be helpful on
864 pimpl_->buffer_list_.emergencyWriteAll();
866 pimpl_->lyx_server_->emergencyCleanup();
867 pimpl_->lyx_server_.reset();
868 pimpl_->lyx_socket_.reset();
873 void LyX::deadKeyBindings(kb_keymap * kbmap)
875 // bindKeyings for transparent handling of deadkeys
876 // The keysyms are gotten from XFree86 X11R6
877 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
878 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
879 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
880 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
881 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
882 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
883 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
884 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
885 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
886 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
887 // nothing with this name
888 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
889 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
890 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
891 // nothing with this name either...
892 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
893 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
894 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
895 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
901 // return true if file does not exist or is older than configure.py.
902 bool needsUpdate(string const & file)
904 static string const configure_script =
905 addName(package().system_support(), "configure.py");
906 string const absfile =
907 addName(package().user_support(), file);
909 return (! fs::exists(absfile))
910 || (fs::last_write_time(configure_script)
911 > fs::last_write_time(absfile));
917 bool LyX::queryUserLyXDir(bool explicit_userdir)
919 // Does user directory exist?
920 if (fs::exists(package().user_support()) &&
921 fs::is_directory(package().user_support())) {
924 return needsUpdate("lyxrc.defaults")
925 || needsUpdate("textclass.lst")
926 || needsUpdate("packages.lst");
929 first_start = !explicit_userdir;
931 // If the user specified explicitly a directory, ask whether
932 // to create it. If the user says "no", then exit.
933 if (explicit_userdir &&
935 _("Missing user LyX directory"),
936 bformat(_("You have specified a non-existent user "
937 "LyX directory, %1$s.\n"
938 "It is needed to keep your own configuration."),
939 from_utf8(package().user_support())),
941 _("&Create directory"),
943 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
944 earlyExit(EXIT_FAILURE);
947 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
948 from_utf8(package().user_support())))
951 if (!createDirectory(package().user_support(), 0755)) {
952 // Failed, so let's exit.
953 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
955 earlyExit(EXIT_FAILURE);
962 bool LyX::readRcFile(string const & name)
964 lyxerr[Debug::INIT] << "About to read " << name << "... ";
966 string const lyxrc_path = libFileSearch(string(), name);
967 if (!lyxrc_path.empty()) {
969 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
971 if (lyxrc.read(lyxrc_path) < 0) {
976 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
982 // Read the ui file `name'
983 bool LyX::readUIFile(string const & name)
993 struct keyword_item uitags[ui_last - 1] = {
994 { "include", ui_include },
995 { "menuset", ui_menuset },
996 { "toolbar", ui_toolbar },
997 { "toolbars", ui_toolbars }
1000 // Ensure that a file is read only once (prevents include loops)
1001 static std::list<string> uifiles;
1002 std::list<string>::const_iterator it = uifiles.begin();
1003 std::list<string>::const_iterator end = uifiles.end();
1004 it = std::find(it, end, name);
1006 lyxerr[Debug::INIT] << "UI file '" << name
1007 << "' has been read already. "
1008 << "Is this an include loop?"
1013 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1015 string const ui_path = libFileSearch("ui", name, "ui");
1017 if (ui_path.empty()) {
1018 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1019 showFileError(name);
1022 uifiles.push_back(name);
1024 lyxerr[Debug::INIT] << "Found " << name
1025 << " in " << ui_path << endl;
1026 LyXLex lex(uitags, ui_last - 1);
1027 lex.setFile(ui_path);
1029 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1033 if (lyxerr.debugging(Debug::PARSER))
1034 lex.printTable(lyxerr);
1036 while (lex.isOK()) {
1037 switch (lex.lex()) {
1040 string const file = lex.getString();
1041 if (!readUIFile(file))
1046 menubackend.read(lex);
1050 toolbarbackend.read(lex);
1054 toolbarbackend.readToolbars(lex);
1058 if (!rtrim(lex.getString()).empty())
1059 lex.printError("LyX::ReadUIFile: "
1060 "Unknown menu tag: `$$Token'");
1068 // Read the languages file `name'
1069 bool LyX::readLanguagesFile(string const & name)
1071 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1073 string const lang_path = libFileSearch(string(), name);
1074 if (lang_path.empty()) {
1075 showFileError(name);
1078 languages.read(lang_path);
1083 // Read the encodings file `name'
1084 bool LyX::readEncodingsFile(string const & name)
1086 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1088 string const enc_path = libFileSearch(string(), name);
1089 if (enc_path.empty()) {
1090 showFileError(name);
1093 encodings.read(enc_path);
1102 /// return the the number of arguments consumed
1103 typedef boost::function<int(string const &, string const &)> cmd_helper;
1105 int parse_dbg(string const & arg, string const &)
1108 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1109 Debug::showTags(lyxerr);
1112 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1114 lyxerr.level(Debug::value(arg));
1115 Debug::showLevel(lyxerr, lyxerr.level());
1120 int parse_help(string const &, string const &)
1123 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1124 "Command line switches (case sensitive):\n"
1125 "\t-help summarize LyX usage\n"
1126 "\t-userdir dir set user directory to dir\n"
1127 "\t-sysdir dir set system directory to dir\n"
1128 "\t-geometry WxH+X+Y set geometry of the main window\n"
1129 "\t-dbg feature[,feature]...\n"
1130 " select the features to debug.\n"
1131 " Type `lyx -dbg' to see the list of features\n"
1132 "\t-x [--execute] command\n"
1133 " where command is a lyx command.\n"
1134 "\t-e [--export] fmt\n"
1135 " where fmt is the export format of choice.\n"
1136 "\t-i [--import] fmt file.xxx\n"
1137 " where fmt is the import format of choice\n"
1138 " and file.xxx is the file to be imported.\n"
1139 "\t-version summarize version and build info\n"
1140 "Check the LyX man page for more details.")) << endl;
1145 int parse_version(string const &, string const &)
1147 lyxerr << "LyX " << lyx_version
1148 << " (" << lyx_release_date << ")" << endl;
1149 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1151 lyxerr << lyx_version_info << endl;
1156 int parse_sysdir(string const & arg, string const &)
1159 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1162 cl_system_support = arg;
1166 int parse_userdir(string const & arg, string const &)
1169 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1172 cl_user_support = arg;
1176 int parse_execute(string const & arg, string const &)
1179 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1186 int parse_export(string const & type, string const &)
1189 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1190 "--export switch")) << endl;
1193 batch = "buffer-export " + type;
1198 int parse_import(string const & type, string const & file)
1201 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1202 "--import switch")) << endl;
1206 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1210 batch = "buffer-import " + type + ' ' + file;
1217 void LyX::easyParse(int & argc, char * argv[])
1219 std::map<string, cmd_helper> cmdmap;
1221 cmdmap["-dbg"] = parse_dbg;
1222 cmdmap["-help"] = parse_help;
1223 cmdmap["--help"] = parse_help;
1224 cmdmap["-version"] = parse_version;
1225 cmdmap["--version"] = parse_version;
1226 cmdmap["-sysdir"] = parse_sysdir;
1227 cmdmap["-userdir"] = parse_userdir;
1228 cmdmap["-x"] = parse_execute;
1229 cmdmap["--execute"] = parse_execute;
1230 cmdmap["-e"] = parse_export;
1231 cmdmap["--export"] = parse_export;
1232 cmdmap["-i"] = parse_import;
1233 cmdmap["--import"] = parse_import;
1235 for (int i = 1; i < argc; ++i) {
1236 std::map<string, cmd_helper>::const_iterator it
1237 = cmdmap.find(argv[i]);
1239 // check for X11 -geometry option
1240 if (support::compare(argv[i], "-geometry") == 0)
1241 geometryOption_ = true;
1243 // don't complain if not found - may be parsed later
1244 if (it == cmdmap.end())
1247 string arg((i + 1 < argc) ? argv[i + 1] : "");
1248 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1250 int const remove = 1 + it->second(arg, arg2);
1252 // Now, remove used arguments by shifting
1253 // the following ones remove places down.
1255 for (int j = i; j < argc; ++j)
1256 argv[j] = argv[j + remove];
1260 batch_command = batch;
1264 FuncStatus getStatus(FuncRequest const & action)
1266 return LyX::ref().lyxFunc().getStatus(action);
1270 void dispatch(FuncRequest const & action)
1272 LyX::ref().lyxFunc().dispatch(action);
1276 BufferList & theBufferList()
1278 return LyX::ref().bufferList();
1282 LyXFunc & theLyXFunc()
1284 return LyX::ref().lyxFunc();
1288 LyXServer & theLyXServer()
1290 // FIXME: this should not be use_gui dependent
1291 BOOST_ASSERT(use_gui);
1292 return LyX::ref().server();
1296 LyXServerSocket & theLyXServerSocket()
1298 // FIXME: this should not be use_gui dependent
1299 BOOST_ASSERT(use_gui);
1300 return LyX::ref().socket();
1304 kb_keymap & theTopLevelKeymap()
1306 BOOST_ASSERT(use_gui);
1307 return LyX::ref().topLevelKeymap();
1315 void assertion_failed(char const* a, char const* b, char const* c, long d)
1317 lyx::lyxerr << "Assertion failed: " << a << ' ' << b << ' ' << c << ' '