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
337 * and the socket callback registering. With Qt4, only QtCore
338 * library would be needed.
339 * When this is done, a server_mode could be created and the following two
340 * line would be moved out from here.
342 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
343 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
344 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
346 // handle the batch commands the user asked for
347 if (!batch_command.empty()) {
348 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
351 exit_status = pimpl_->application_->start(batch_command);
352 // Kill the application object before exiting. This avoid crash
354 pimpl_->application_.reset();
355 // Restore original font resources after Application is destroyed.
356 support::restoreFontResources();
359 // FIXME: create a ConsoleApplication
367 void LyX::prepareExit()
369 // Set a flag that we do quitting from the program,
370 // so no refreshes are necessary.
373 // close buffers first
374 pimpl_->buffer_list_.closeAll();
376 // do any other cleanup procedures now
377 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
379 if (!destroyDir(package().temp_dir())) {
380 docstring const msg =
381 bformat(_("Unable to remove the temporary directory %1$s"),
382 from_utf8(package().temp_dir()));
383 Alert::warning(_("Unable to remove temporary directory"), msg);
388 void LyX::earlyExit(int status)
390 BOOST_ASSERT(pimpl_->application_.get());
391 // LyX::pimpl_::application_ is not initialised at this
392 // point so it's safe to just exit after some cleanup.
398 void LyX::quit(bool noask)
400 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
403 if (!noask && !pimpl_->buffer_list_.quitWriteAll())
406 pimpl_->session_->writeFile();
412 pimpl_->lyx_server_.reset();
413 pimpl_->lyx_socket_.reset();
414 pimpl_->application_->exit(0);
419 int LyX::execBatchCommands(int & argc, char * argv[],
420 vector<string> & files)
422 // check for any spurious extra arguments
423 // other than documents
424 for (int argi = 1; argi < argc ; ++argi) {
425 if (argv[argi][0] == '-') {
427 bformat(_("Wrong command line option `%1$s'. Exiting."),
428 from_utf8(argv[argi]))) << endl;
433 // Initialization of LyX (reads lyxrc and more)
434 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
435 bool success = init();
436 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
440 for (int argi = argc - 1; argi >= 1; --argi)
441 files.push_back(os::internal_path(argv[argi]));
444 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
446 // Execute batch commands if available
447 if (!batch_command.empty()) {
449 lyxerr[Debug::INIT] << "About to handle -x '"
450 << batch_command << '\'' << endl;
452 Buffer * last_loaded = 0;
454 vector<string>::const_iterator it = files.begin();
455 vector<string>::const_iterator end = files.end();
457 for (; it != end; ++it) {
458 // get absolute path of file and add ".lyx" to
459 // the filename if necessary
460 string s = fileSearch(string(), *it, "lyx");
462 Buffer * const b = newFile(*it, string(), true);
466 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
467 if (loadLyXFile(buf, s)) {
469 ErrorList const & el = buf->errorList("Parse");
471 for_each(el.begin(), el.end(),
472 boost::bind(&LyX::printError, this, _1));
475 pimpl_->buffer_list_.release(buf);
479 // try to dispatch to last loaded buffer first
482 if (last_loaded->dispatch(batch_command, &success)) {
487 files.clear(); // the files are already loaded
494 void LyX::restoreGuiSession(vector<string> const & files)
496 LyXView * view = newLyXView();
499 for_each(files.begin(), files.end(),
500 bind(&LyXView::loadLyXFile, view, _1, true));
502 // if a file is specified, I assume that user wants to edit *that* file
503 if (files.empty() && lyxrc.load_session) {
504 vector<string> const & lastopened = pimpl_->session_->lastOpenedFiles();
505 // do not add to the lastfile list since these files are restored from
506 // last seesion, and should be already there (regular files), or should
507 // not be added at all (help files).
508 for_each(lastopened.begin(), lastopened.end(),
509 bind(&LyXView::loadLyXFile, view, _1, false));
511 // clear this list to save a few bytes of RAM
512 pimpl_->session_->clearLastOpenedFiles();
516 LyXView * LyX::newLyXView()
518 // determine windows size and position, from lyxrc and/or session
520 unsigned int width = 690;
521 unsigned int height = 510;
522 bool maximize = false;
524 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
525 width = lyxrc.geometry_width;
526 height = lyxrc.geometry_height;
528 // if lyxrc returns (0,0), then use session info
530 string val = session().loadSessionInfo("WindowWidth");
532 width = convert<unsigned int>(val);
533 val = session().loadSessionInfo("WindowHeight");
535 height = convert<unsigned int>(val);
536 if (session().loadSessionInfo("WindowIsMaximized") == "yes")
540 // if user wants to restore window position
543 if (lyxrc.geometry_xysaved) {
544 string val = session().loadSessionInfo("WindowPosX");
546 posx = convert<int>(val);
547 val = session().loadSessionInfo("WindowPosY");
549 posy = convert<int>(val);
552 if (geometryOption_) {
556 // create the main window
557 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
558 ref().addLyXView(view);
566 The SIGHUP signal does not exist on Windows and does not need to be handled.
568 Windows handles SIGFPE and SIGSEGV signals as expected.
570 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
571 cause a new thread to be spawned. This may well result in unexpected
572 behaviour by the single-threaded LyX.
574 SIGTERM signals will come only from another process actually sending
575 that signal using 'raise' in Windows' POSIX compatability layer. It will
576 not come from the general "terminate process" methods that everyone
577 actually uses (and which can't be trapped). Killing an app 'politely' on
578 Windows involves first sending a WM_CLOSE message, something that is
579 caught already by the Qt frontend.
581 For more information see:
583 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
584 ...signals are mostly useless on Windows for a variety of reasons that are
587 'UNIX Application Migration Guide, Chapter 9'
588 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
590 'How To Terminate an Application "Cleanly" in Win32'
591 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
595 static void error_handler(int err_sig)
597 // Throw away any signals other than the first one received.
598 static sig_atomic_t handling_error = false;
601 handling_error = true;
603 // We have received a signal indicating a fatal error, so
604 // try and save the data ASAP.
605 LyX::cref().emergencyCleanup();
607 // These lyxerr calls may or may not work:
609 // Signals are asynchronous, so the main program may be in a very
610 // fragile state when a signal is processed and thus while a signal
611 // handler function executes.
612 // In general, therefore, we should avoid performing any
613 // I/O operations or calling most library and system functions from
616 // This shouldn't matter here, however, as we've already invoked
621 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
625 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
628 lyxerr << "\nlyx: SIGSEGV signal caught\n"
629 "Sorry, you have found a bug in LyX. "
630 "Please read the bug-reporting instructions "
631 "in Help->Introduction and send us a bug report, "
632 "if necessary. Thanks !\nBye." << endl;
640 // Deinstall the signal handlers
642 signal(SIGHUP, SIG_DFL);
644 signal(SIGINT, SIG_DFL);
645 signal(SIGFPE, SIG_DFL);
646 signal(SIGSEGV, SIG_DFL);
647 signal(SIGTERM, SIG_DFL);
650 if (err_sig == SIGSEGV ||
651 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
653 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
662 void LyX::printError(ErrorItem const & ei)
664 docstring tmp = _("LyX: ") + ei.error + char_type(':')
666 std::cerr << to_utf8(tmp) << std::endl;
670 void LyX::initGuiFont()
672 if (lyxrc.roman_font_name.empty())
673 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
675 if (lyxrc.sans_font_name.empty())
676 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
678 if (lyxrc.typewriter_font_name.empty())
679 lyxrc.typewriter_font_name
680 = pimpl_->application_->typewriterFontName();
687 signal(SIGHUP, error_handler);
689 signal(SIGFPE, error_handler);
690 signal(SIGSEGV, error_handler);
691 signal(SIGINT, error_handler);
692 signal(SIGTERM, error_handler);
693 // SIGPIPE can be safely ignored.
695 lyxrc.tempdir_path = package().temp_dir();
696 lyxrc.document_path = package().document_dir();
698 if (lyxrc.template_path.empty()) {
699 lyxrc.template_path = addPath(package().system_support(),
704 // Read configuration files
707 // This one may have been distributed along with LyX.
708 if (!readRcFile("lyxrc.dist"))
711 // Set the PATH correctly.
712 #if !defined (USE_POSIX_PACKAGING)
713 // Add the directory containing the LyX executable to the path
714 // so that LyX can find things like tex2lyx.
715 if (package().build_support().empty())
716 prependEnvPath("PATH", package().binary_dir());
718 if (!lyxrc.path_prefix.empty())
719 prependEnvPath("PATH", lyxrc.path_prefix);
721 // Check that user LyX directory is ok.
722 if (queryUserLyXDir(package().explicit_user_support()))
723 reconfigureUserLyXDir();
725 // no need for a splash when there is no GUI
730 // This one is generated in user_support directory by lib/configure.py.
731 if (!readRcFile("lyxrc.defaults"))
734 // Query the OS to know what formats are viewed natively
735 formats.setAutoOpen();
737 system_lyxrc = lyxrc;
738 system_formats = formats;
739 system_converters = converters;
740 system_movers = movers;
741 system_lcolor = lcolor;
743 // This one is edited through the preferences dialog.
744 if (!readRcFile("preferences"))
747 if (!readEncodingsFile("encodings"))
749 if (!readLanguagesFile("languages"))
753 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
759 pimpl_->toplevel_keymap_.reset(new kb_keymap);
760 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
761 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
763 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
766 if (!readUIFile(lyxrc.ui_file))
770 if (lyxerr.debugging(Debug::LYXRC))
773 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
774 if (!lyxrc.path_prefix.empty())
775 prependEnvPath("PATH", lyxrc.path_prefix);
777 if (fs::exists(lyxrc.document_path) &&
778 fs::is_directory(lyxrc.document_path))
779 package().document_dir() = lyxrc.document_path;
781 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
782 if (package().temp_dir().empty()) {
783 Alert::error(_("Could not create temporary directory"),
784 bformat(_("Could not create a temporary directory in\n"
785 "%1$s. Make sure that this\n"
786 "path exists and is writable and try again."),
787 from_utf8(lyxrc.tempdir_path)));
788 // createLyXTmpDir() tries sufficiently hard to create a
789 // usable temp dir, so the probability to come here is
790 // close to zero. We therefore don't try to overcome this
791 // problem with e.g. asking the user for a new path and
792 // trying again but simply exit.
796 if (lyxerr.debugging(Debug::INIT)) {
797 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
800 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
801 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
806 void LyX::defaultKeyBindings(kb_keymap * kbmap)
808 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
809 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
810 kbmap->bind("Up", FuncRequest(LFUN_UP));
811 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
813 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
814 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
815 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
816 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
818 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
819 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
820 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
821 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
823 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
824 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
826 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
827 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
829 // kbmap->bindings to enable the use of the numeric keypad
831 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
832 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
833 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
834 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
835 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
836 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
837 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
838 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
839 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
840 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
841 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
842 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
843 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
844 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
845 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
846 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
847 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
848 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
849 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
850 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
851 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
852 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
853 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
854 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
858 void LyX::emergencyCleanup() const
860 // what to do about tmpfiles is non-obvious. we would
861 // like to delete any we find, but our lyxdir might
862 // contain documents etc. which might be helpful on
865 pimpl_->buffer_list_.emergencyWriteAll();
867 pimpl_->lyx_server_->emergencyCleanup();
868 pimpl_->lyx_server_.reset();
869 pimpl_->lyx_socket_.reset();
874 void LyX::deadKeyBindings(kb_keymap * kbmap)
876 // bindKeyings for transparent handling of deadkeys
877 // The keysyms are gotten from XFree86 X11R6
878 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
879 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
880 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
881 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
882 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
883 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
884 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
885 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
886 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
887 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
888 // nothing with this name
889 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
890 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
891 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
892 // nothing with this name either...
893 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
894 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
895 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
896 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
902 // return true if file does not exist or is older than configure.py.
903 bool needsUpdate(string const & file)
905 static string const configure_script =
906 addName(package().system_support(), "configure.py");
907 string const absfile =
908 addName(package().user_support(), file);
910 return (! fs::exists(absfile))
911 || (fs::last_write_time(configure_script)
912 > fs::last_write_time(absfile));
918 bool LyX::queryUserLyXDir(bool explicit_userdir)
920 // Does user directory exist?
921 if (fs::exists(package().user_support()) &&
922 fs::is_directory(package().user_support())) {
925 return needsUpdate("lyxrc.defaults")
926 || needsUpdate("textclass.lst")
927 || needsUpdate("packages.lst");
930 first_start = !explicit_userdir;
932 // If the user specified explicitly a directory, ask whether
933 // to create it. If the user says "no", then exit.
934 if (explicit_userdir &&
936 _("Missing user LyX directory"),
937 bformat(_("You have specified a non-existent user "
938 "LyX directory, %1$s.\n"
939 "It is needed to keep your own configuration."),
940 from_utf8(package().user_support())),
942 _("&Create directory"),
944 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
945 earlyExit(EXIT_FAILURE);
948 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
949 from_utf8(package().user_support())))
952 if (!createDirectory(package().user_support(), 0755)) {
953 // Failed, so let's exit.
954 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
956 earlyExit(EXIT_FAILURE);
963 bool LyX::readRcFile(string const & name)
965 lyxerr[Debug::INIT] << "About to read " << name << "... ";
967 string const lyxrc_path = libFileSearch(string(), name);
968 if (!lyxrc_path.empty()) {
970 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
972 if (lyxrc.read(lyxrc_path) < 0) {
977 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
983 // Read the ui file `name'
984 bool LyX::readUIFile(string const & name)
994 struct keyword_item uitags[ui_last - 1] = {
995 { "include", ui_include },
996 { "menuset", ui_menuset },
997 { "toolbar", ui_toolbar },
998 { "toolbars", ui_toolbars }
1001 // Ensure that a file is read only once (prevents include loops)
1002 static std::list<string> uifiles;
1003 std::list<string>::const_iterator it = uifiles.begin();
1004 std::list<string>::const_iterator end = uifiles.end();
1005 it = std::find(it, end, name);
1007 lyxerr[Debug::INIT] << "UI file '" << name
1008 << "' has been read already. "
1009 << "Is this an include loop?"
1014 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1016 string const ui_path = libFileSearch("ui", name, "ui");
1018 if (ui_path.empty()) {
1019 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1020 showFileError(name);
1023 uifiles.push_back(name);
1025 lyxerr[Debug::INIT] << "Found " << name
1026 << " in " << ui_path << endl;
1027 LyXLex lex(uitags, ui_last - 1);
1028 lex.setFile(ui_path);
1030 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1034 if (lyxerr.debugging(Debug::PARSER))
1035 lex.printTable(lyxerr);
1037 while (lex.isOK()) {
1038 switch (lex.lex()) {
1041 string const file = lex.getString();
1042 if (!readUIFile(file))
1047 menubackend.read(lex);
1051 toolbarbackend.read(lex);
1055 toolbarbackend.readToolbars(lex);
1059 if (!rtrim(lex.getString()).empty())
1060 lex.printError("LyX::ReadUIFile: "
1061 "Unknown menu tag: `$$Token'");
1069 // Read the languages file `name'
1070 bool LyX::readLanguagesFile(string const & name)
1072 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1074 string const lang_path = libFileSearch(string(), name);
1075 if (lang_path.empty()) {
1076 showFileError(name);
1079 languages.read(lang_path);
1084 // Read the encodings file `name'
1085 bool LyX::readEncodingsFile(string const & name)
1087 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1089 string const enc_path = libFileSearch(string(), name);
1090 if (enc_path.empty()) {
1091 showFileError(name);
1094 encodings.read(enc_path);
1103 /// return the the number of arguments consumed
1104 typedef boost::function<int(string const &, string const &)> cmd_helper;
1106 int parse_dbg(string const & arg, string const &)
1109 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1110 Debug::showTags(lyxerr);
1113 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1115 lyxerr.level(Debug::value(arg));
1116 Debug::showLevel(lyxerr, lyxerr.level());
1121 int parse_help(string const &, string const &)
1124 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1125 "Command line switches (case sensitive):\n"
1126 "\t-help summarize LyX usage\n"
1127 "\t-userdir dir set user directory to dir\n"
1128 "\t-sysdir dir set system directory to dir\n"
1129 "\t-geometry WxH+X+Y set geometry of the main window\n"
1130 "\t-dbg feature[,feature]...\n"
1131 " select the features to debug.\n"
1132 " Type `lyx -dbg' to see the list of features\n"
1133 "\t-x [--execute] command\n"
1134 " where command is a lyx command.\n"
1135 "\t-e [--export] fmt\n"
1136 " where fmt is the export format of choice.\n"
1137 "\t-i [--import] fmt file.xxx\n"
1138 " where fmt is the import format of choice\n"
1139 " and file.xxx is the file to be imported.\n"
1140 "\t-version summarize version and build info\n"
1141 "Check the LyX man page for more details.")) << endl;
1146 int parse_version(string const &, string const &)
1148 lyxerr << "LyX " << lyx_version
1149 << " (" << lyx_release_date << ")" << endl;
1150 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1152 lyxerr << lyx_version_info << endl;
1157 int parse_sysdir(string const & arg, string const &)
1160 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1163 cl_system_support = arg;
1167 int parse_userdir(string const & arg, string const &)
1170 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1173 cl_user_support = arg;
1177 int parse_execute(string const & arg, string const &)
1180 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1187 int parse_export(string const & type, string const &)
1190 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1191 "--export switch")) << endl;
1194 batch = "buffer-export " + type;
1199 int parse_import(string const & type, string const & file)
1202 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1203 "--import switch")) << endl;
1207 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1211 batch = "buffer-import " + type + ' ' + file;
1218 void LyX::easyParse(int & argc, char * argv[])
1220 std::map<string, cmd_helper> cmdmap;
1222 cmdmap["-dbg"] = parse_dbg;
1223 cmdmap["-help"] = parse_help;
1224 cmdmap["--help"] = parse_help;
1225 cmdmap["-version"] = parse_version;
1226 cmdmap["--version"] = parse_version;
1227 cmdmap["-sysdir"] = parse_sysdir;
1228 cmdmap["-userdir"] = parse_userdir;
1229 cmdmap["-x"] = parse_execute;
1230 cmdmap["--execute"] = parse_execute;
1231 cmdmap["-e"] = parse_export;
1232 cmdmap["--export"] = parse_export;
1233 cmdmap["-i"] = parse_import;
1234 cmdmap["--import"] = parse_import;
1236 for (int i = 1; i < argc; ++i) {
1237 std::map<string, cmd_helper>::const_iterator it
1238 = cmdmap.find(argv[i]);
1240 // check for X11 -geometry option
1241 if (support::compare(argv[i], "-geometry") == 0)
1242 geometryOption_ = true;
1244 // don't complain if not found - may be parsed later
1245 if (it == cmdmap.end())
1248 string arg((i + 1 < argc) ? argv[i + 1] : "");
1249 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1251 int const remove = 1 + it->second(arg, arg2);
1253 // Now, remove used arguments by shifting
1254 // the following ones remove places down.
1256 for (int j = i; j < argc; ++j)
1257 argv[j] = argv[j + remove];
1261 batch_command = batch;
1265 FuncStatus getStatus(FuncRequest const & action)
1267 return LyX::ref().lyxFunc().getStatus(action);
1271 void dispatch(FuncRequest const & action)
1273 LyX::ref().lyxFunc().dispatch(action);
1277 BufferList & theBufferList()
1279 return LyX::ref().bufferList();
1283 LyXFunc & theLyXFunc()
1285 return LyX::ref().lyxFunc();
1289 LyXServer & theLyXServer()
1291 // FIXME: this should not be use_gui dependent
1292 BOOST_ASSERT(use_gui);
1293 return LyX::ref().server();
1297 LyXServerSocket & theLyXServerSocket()
1299 // FIXME: this should not be use_gui dependent
1300 BOOST_ASSERT(use_gui);
1301 return LyX::ref().socket();
1305 kb_keymap & theTopLevelKeymap()
1307 BOOST_ASSERT(use_gui);
1308 return LyX::ref().topLevelKeymap();
1316 void assertion_failed(char const* a, char const* b, char const* c, long d)
1318 lyx::lyxerr << "Assertion failed: " << a << ' ' << b << ' ' << c << ' '