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::fileSearch;
78 using support::getEnv;
79 using support::i18nLibFileSearch;
80 using support::libFileSearch;
81 using support::package;
82 using support::prependEnvPath;
84 using support::Systemcall;
86 namespace Alert = frontend::Alert;
87 namespace os = support::os;
88 namespace fs = boost::filesystem;
95 #ifndef CXX_GLOBAL_CSTD
102 frontend::Application * theApp = 0;
104 /// are we using the GUI at all?
106 * We default to true and this is changed to false when the export feature is used.
113 // Filled with the command line arguments "foo" of "-sysdir foo" or
115 string cl_system_support;
116 string cl_user_support;
118 LyX * singleton_ = 0;
120 void showFileError(string const & error)
122 Alert::warning(_("Could not read configuration file"),
123 bformat(_("Error while reading the configuration file\n%1$s.\n"
124 "Please check your installation."), from_utf8(error)));
128 void reconfigureUserLyXDir()
130 string const configure_command = package().configure_command();
132 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
133 support::Path p(package().user_support());
135 one.startscript(Systemcall::Wait, configure_command);
136 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
142 /// The main application class private implementation.
143 struct LyX::Singletons
145 Singletons(): iconv(ucs4_codeset, "UTF-8")
148 /// our function handler
151 BufferList buffer_list_;
153 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
155 boost::scoped_ptr<LyXServer> lyx_server_;
157 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
159 boost::scoped_ptr<frontend::Application> application_;
160 /// lyx session, containing lastfiles, lastfilepos, and lastopened
161 boost::scoped_ptr<Session> session_;
164 IconvProcessor iconv;
170 // Static data are not treated in the same way at all on the Mac (and
171 // the LyX singleton has static methods). This is the reason why the
172 // exit command on the Mac bypasses our dispatch machinery altogether.
173 // On Linux and Windows we won't pass a second time through quit()
174 // because quitting will already be set to true.
182 BOOST_ASSERT(singleton_);
187 LyX const & LyX::cref()
189 BOOST_ASSERT(singleton_);
195 : first_start(false), geometryOption_(false)
198 pimpl_.reset(new Singletons);
202 BufferList & LyX::bufferList()
204 return pimpl_->buffer_list_;
208 BufferList const & LyX::bufferList() const
210 return pimpl_->buffer_list_;
214 Session & LyX::session()
216 BOOST_ASSERT(pimpl_->session_.get());
217 return *pimpl_->session_.get();
221 Session const & LyX::session() const
223 BOOST_ASSERT(pimpl_->session_.get());
224 return *pimpl_->session_.get();
228 LyXFunc & LyX::lyxFunc()
230 return pimpl_->lyxfunc_;
234 LyXFunc const & LyX::lyxFunc() const
236 return pimpl_->lyxfunc_;
240 LyXServer & LyX::server()
242 BOOST_ASSERT(pimpl_->lyx_server_.get());
243 return *pimpl_->lyx_server_.get();
247 LyXServer const & LyX::server() const
249 BOOST_ASSERT(pimpl_->lyx_server_.get());
250 return *pimpl_->lyx_server_.get();
254 LyXServerSocket & LyX::socket()
256 BOOST_ASSERT(pimpl_->lyx_socket_.get());
257 return *pimpl_->lyx_socket_.get();
261 LyXServerSocket const & LyX::socket() const
263 BOOST_ASSERT(pimpl_->lyx_socket_.get());
264 return *pimpl_->lyx_socket_.get();
268 frontend::Application & LyX::application()
270 BOOST_ASSERT(pimpl_->application_.get());
271 return *pimpl_->application_.get();
275 frontend::Application const & LyX::application() const
277 BOOST_ASSERT(pimpl_->application_.get());
278 return *pimpl_->application_.get();
282 kb_keymap & LyX::topLevelKeymap()
284 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
285 return *pimpl_->toplevel_keymap_.get();
289 IconvProcessor & LyX::iconvProcessor()
291 return pimpl_->iconv;
295 kb_keymap const & LyX::topLevelKeymap() const
297 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
298 return *pimpl_->toplevel_keymap_.get();
302 Buffer const * const LyX::updateInset(InsetBase const * inset) const
307 Buffer const * buffer_ptr = 0;
308 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
309 vector<int>::const_iterator it = view_ids.begin();
310 vector<int>::const_iterator const end = view_ids.end();
311 for (; it != end; ++it) {
313 pimpl_->application_->gui().view(*it).updateInset(inset);
321 int LyX::exec(int & argc, char * argv[])
323 // Here we need to parse the command line. At least
324 // we need to parse for "-dbg" and "-help"
325 easyParse(argc, argv);
327 support::init_package(argv[0], cl_system_support, cl_user_support,
328 support::top_build_dir_is_one_level_up);
330 vector<string> files;
331 int exit_status = execBatchCommands(argc, argv, files);
337 // Force adding of font path _before_ Application is initialized
338 support::addFontResources();
339 pimpl_->application_.reset(createApplication(argc, argv));
341 // FIXME: this global pointer should probably go.
342 theApp = pimpl_->application_.get();
343 restoreGuiSession(files);
344 // Start the real execution loop.
347 /* Create a CoreApplication class that will provide the main event loop
348 * and the socket callback registering. With Qt4, only QtCore
349 * library would be needed.
350 * When this is done, a server_mode could be created and the following two
351 * line would be moved out from here.
353 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
354 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
355 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
357 // handle the batch commands the user asked for
358 if (!batch_command.empty()) {
359 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
362 exit_status = pimpl_->application_->start(batch_command);
363 // Kill the application object before exiting. This avoid crash
365 pimpl_->application_.reset();
366 // Restore original font resources after Application is destroyed.
367 support::restoreFontResources();
370 // FIXME: create a ConsoleApplication
378 void LyX::prepareExit()
380 // Set a flag that we do quitting from the program,
381 // so no refreshes are necessary.
384 // close buffers first
385 pimpl_->buffer_list_.closeAll();
387 // do any other cleanup procedures now
388 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
390 if (!destroyDir(package().temp_dir())) {
391 docstring const msg =
392 bformat(_("Unable to remove the temporary directory %1$s"),
393 from_utf8(package().temp_dir()));
394 Alert::warning(_("Unable to remove temporary directory"), msg);
399 void LyX::earlyExit(int status)
401 BOOST_ASSERT(pimpl_->application_.get());
402 // LyX::pimpl_::application_ is not initialised at this
403 // point so it's safe to just exit after some cleanup.
411 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
416 pimpl_->session_->writeFile();
417 pimpl_->lyx_server_.reset();
418 pimpl_->lyx_socket_.reset();
419 pimpl_->application_->exit(0);
425 int LyX::execBatchCommands(int & argc, char * argv[],
426 vector<string> & files)
428 // check for any spurious extra arguments
429 // other than documents
430 for (int argi = 1; argi < argc ; ++argi) {
431 if (argv[argi][0] == '-') {
433 bformat(_("Wrong command line option `%1$s'. Exiting."),
434 from_utf8(argv[argi]))) << endl;
439 // Initialization of LyX (reads lyxrc and more)
440 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
441 bool success = init();
442 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
446 for (int argi = argc - 1; argi >= 1; --argi)
447 files.push_back(os::internal_path(argv[argi]));
450 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
452 // Execute batch commands if available
453 if (!batch_command.empty()) {
455 lyxerr[Debug::INIT] << "About to handle -x '"
456 << batch_command << '\'' << endl;
458 Buffer * last_loaded = 0;
460 vector<string>::const_iterator it = files.begin();
461 vector<string>::const_iterator end = files.end();
463 for (; it != end; ++it) {
464 // get absolute path of file and add ".lyx" to
465 // the filename if necessary
466 string s = fileSearch(string(), *it, "lyx");
468 Buffer * const b = newFile(*it, string(), true);
472 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
473 if (loadLyXFile(buf, s)) {
475 ErrorList const & el = buf->errorList("Parse");
477 for_each(el.begin(), el.end(),
478 boost::bind(&LyX::printError, this, _1));
481 pimpl_->buffer_list_.release(buf);
485 // try to dispatch to last loaded buffer first
488 if (last_loaded->dispatch(batch_command, &success)) {
493 files.clear(); // the files are already loaded
500 void LyX::restoreGuiSession(vector<string> const & files)
502 LyXView * view = newLyXView();
505 for_each(files.begin(), files.end(),
506 bind(&LyXView::loadLyXFile, view, _1, true));
508 // if a file is specified, I assume that user wants to edit *that* file
509 if (files.empty() && lyxrc.load_session) {
510 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
511 // do not add to the lastfile list since these files are restored from
512 // last seesion, and should be already there (regular files), or should
513 // not be added at all (help files).
514 for_each(lastopened.begin(), lastopened.end(),
515 bind(&LyXView::loadLyXFile, view, _1, false));
517 // clear this list to save a few bytes of RAM
518 pimpl_->session_->lastOpened().clear();
522 LyXView * LyX::newLyXView()
527 // determine windows size and position, from lyxrc and/or session
529 unsigned int width = 690;
530 unsigned int height = 510;
531 bool maximize = false;
533 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
534 width = lyxrc.geometry_width;
535 height = lyxrc.geometry_height;
537 // if lyxrc returns (0,0), then use session info
539 string val = session().sessionInfo().load("WindowWidth");
541 width = convert<unsigned int>(val);
542 val = session().sessionInfo().load("WindowHeight");
544 height = convert<unsigned int>(val);
545 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
549 // if user wants to restore window position
552 if (lyxrc.geometry_xysaved) {
553 string val = session().sessionInfo().load("WindowPosX");
555 posx = convert<int>(val);
556 val = session().sessionInfo().load("WindowPosY");
558 posy = convert<int>(val);
561 if (geometryOption_) {
565 // create the main window
566 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
574 The SIGHUP signal does not exist on Windows and does not need to be handled.
576 Windows handles SIGFPE and SIGSEGV signals as expected.
578 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
579 cause a new thread to be spawned. This may well result in unexpected
580 behaviour by the single-threaded LyX.
582 SIGTERM signals will come only from another process actually sending
583 that signal using 'raise' in Windows' POSIX compatability layer. It will
584 not come from the general "terminate process" methods that everyone
585 actually uses (and which can't be trapped). Killing an app 'politely' on
586 Windows involves first sending a WM_CLOSE message, something that is
587 caught already by the Qt frontend.
589 For more information see:
591 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
592 ...signals are mostly useless on Windows for a variety of reasons that are
595 'UNIX Application Migration Guide, Chapter 9'
596 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
598 'How To Terminate an Application "Cleanly" in Win32'
599 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
603 static void error_handler(int err_sig)
605 // Throw away any signals other than the first one received.
606 static sig_atomic_t handling_error = false;
609 handling_error = true;
611 // We have received a signal indicating a fatal error, so
612 // try and save the data ASAP.
613 LyX::cref().emergencyCleanup();
615 // These lyxerr calls may or may not work:
617 // Signals are asynchronous, so the main program may be in a very
618 // fragile state when a signal is processed and thus while a signal
619 // handler function executes.
620 // In general, therefore, we should avoid performing any
621 // I/O operations or calling most library and system functions from
624 // This shouldn't matter here, however, as we've already invoked
629 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
633 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
636 lyxerr << "\nlyx: SIGSEGV signal caught\n"
637 "Sorry, you have found a bug in LyX. "
638 "Please read the bug-reporting instructions "
639 "in Help->Introduction and send us a bug report, "
640 "if necessary. Thanks !\nBye." << endl;
648 // Deinstall the signal handlers
650 signal(SIGHUP, SIG_DFL);
652 signal(SIGINT, SIG_DFL);
653 signal(SIGFPE, SIG_DFL);
654 signal(SIGSEGV, SIG_DFL);
655 signal(SIGTERM, SIG_DFL);
658 if (err_sig == SIGSEGV ||
659 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
661 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
670 void LyX::printError(ErrorItem const & ei)
672 docstring tmp = _("LyX: ") + ei.error + char_type(':')
674 std::cerr << to_utf8(tmp) << std::endl;
678 void LyX::initGuiFont()
680 if (lyxrc.roman_font_name.empty())
681 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
683 if (lyxrc.sans_font_name.empty())
684 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
686 if (lyxrc.typewriter_font_name.empty())
687 lyxrc.typewriter_font_name
688 = pimpl_->application_->typewriterFontName();
695 signal(SIGHUP, error_handler);
697 signal(SIGFPE, error_handler);
698 signal(SIGSEGV, error_handler);
699 signal(SIGINT, error_handler);
700 signal(SIGTERM, error_handler);
701 // SIGPIPE can be safely ignored.
703 lyxrc.tempdir_path = package().temp_dir();
704 lyxrc.document_path = package().document_dir();
706 if (lyxrc.template_path.empty()) {
707 lyxrc.template_path = addPath(package().system_support(),
712 // Read configuration files
715 // This one may have been distributed along with LyX.
716 if (!readRcFile("lyxrc.dist"))
719 // Set the PATH correctly.
720 #if !defined (USE_POSIX_PACKAGING)
721 // Add the directory containing the LyX executable to the path
722 // so that LyX can find things like tex2lyx.
723 if (package().build_support().empty())
724 prependEnvPath("PATH", package().binary_dir());
726 if (!lyxrc.path_prefix.empty())
727 prependEnvPath("PATH", lyxrc.path_prefix);
729 // Check that user LyX directory is ok.
730 if (queryUserLyXDir(package().explicit_user_support()))
731 reconfigureUserLyXDir();
733 // no need for a splash when there is no GUI
738 // This one is generated in user_support directory by lib/configure.py.
739 if (!readRcFile("lyxrc.defaults"))
742 // Query the OS to know what formats are viewed natively
743 formats.setAutoOpen();
745 // Read lyxrc.dist again to be able to override viewer auto-detection.
746 readRcFile("lyxrc.dist");
748 system_lyxrc = lyxrc;
749 system_formats = formats;
750 system_converters = converters;
751 system_movers = movers;
752 system_lcolor = lcolor;
754 // This one is edited through the preferences dialog.
755 if (!readRcFile("preferences"))
758 if (!readEncodingsFile("encodings"))
760 if (!readLanguagesFile("languages"))
764 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
770 pimpl_->toplevel_keymap_.reset(new kb_keymap);
771 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
772 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
774 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
777 if (!readUIFile(lyxrc.ui_file))
781 if (lyxerr.debugging(Debug::LYXRC))
784 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
785 if (!lyxrc.path_prefix.empty())
786 prependEnvPath("PATH", lyxrc.path_prefix);
788 if (fs::exists(lyxrc.document_path) &&
789 fs::is_directory(lyxrc.document_path))
790 package().document_dir() = lyxrc.document_path;
792 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
793 if (package().temp_dir().empty()) {
794 Alert::error(_("Could not create temporary directory"),
795 bformat(_("Could not create a temporary directory in\n"
796 "%1$s. Make sure that this\n"
797 "path exists and is writable and try again."),
798 from_utf8(lyxrc.tempdir_path)));
799 // createLyXTmpDir() tries sufficiently hard to create a
800 // usable temp dir, so the probability to come here is
801 // close to zero. We therefore don't try to overcome this
802 // problem with e.g. asking the user for a new path and
803 // trying again but simply exit.
807 if (lyxerr.debugging(Debug::INIT)) {
808 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
811 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
812 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
814 // This must happen after package initialization and after lyxrc is
815 // read, therefore it can't be done by a static object.
816 ConverterCache::init();
822 void LyX::defaultKeyBindings(kb_keymap * kbmap)
824 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
825 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
826 kbmap->bind("Up", FuncRequest(LFUN_UP));
827 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
829 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
830 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
831 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
832 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
834 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
835 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
836 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
837 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
839 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
840 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
842 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
843 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
845 // kbmap->bindings to enable the use of the numeric keypad
847 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
848 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
849 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
850 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
851 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
852 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
853 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
854 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
855 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
856 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
857 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
858 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
859 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
860 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
861 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
862 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
863 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
864 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
865 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
866 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
867 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
868 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
869 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
870 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
874 void LyX::emergencyCleanup() const
876 // what to do about tmpfiles is non-obvious. we would
877 // like to delete any we find, but our lyxdir might
878 // contain documents etc. which might be helpful on
881 pimpl_->buffer_list_.emergencyWriteAll();
883 pimpl_->lyx_server_->emergencyCleanup();
884 pimpl_->lyx_server_.reset();
885 pimpl_->lyx_socket_.reset();
890 void LyX::deadKeyBindings(kb_keymap * kbmap)
892 // bindKeyings for transparent handling of deadkeys
893 // The keysyms are gotten from XFree86 X11R6
894 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
895 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
896 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
897 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
898 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
899 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
900 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
901 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
902 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
903 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
904 // nothing with this name
905 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
906 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
907 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
908 // nothing with this name either...
909 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
910 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
911 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
912 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
918 // return true if file does not exist or is older than configure.py.
919 bool needsUpdate(string const & file)
921 static string const configure_script =
922 addName(package().system_support(), "configure.py");
923 string const absfile =
924 addName(package().user_support(), file);
926 return (! fs::exists(absfile))
927 || (fs::last_write_time(configure_script)
928 > fs::last_write_time(absfile));
934 bool LyX::queryUserLyXDir(bool explicit_userdir)
936 // Does user directory exist?
937 if (fs::exists(package().user_support()) &&
938 fs::is_directory(package().user_support())) {
941 return needsUpdate("lyxrc.defaults")
942 || needsUpdate("textclass.lst")
943 || needsUpdate("packages.lst");
946 first_start = !explicit_userdir;
948 // If the user specified explicitly a directory, ask whether
949 // to create it. If the user says "no", then exit.
950 if (explicit_userdir &&
952 _("Missing user LyX directory"),
953 bformat(_("You have specified a non-existent user "
954 "LyX directory, %1$s.\n"
955 "It is needed to keep your own configuration."),
956 from_utf8(package().user_support())),
958 _("&Create directory"),
960 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
961 earlyExit(EXIT_FAILURE);
964 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
965 from_utf8(package().user_support())))
968 if (!createDirectory(package().user_support(), 0755)) {
969 // Failed, so let's exit.
970 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
972 earlyExit(EXIT_FAILURE);
979 bool LyX::readRcFile(string const & name)
981 lyxerr[Debug::INIT] << "About to read " << name << "... ";
983 string const lyxrc_path = libFileSearch(string(), name);
984 if (!lyxrc_path.empty()) {
986 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
988 if (lyxrc.read(lyxrc_path) < 0) {
993 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
999 // Read the ui file `name'
1000 bool LyX::readUIFile(string const & name)
1010 struct keyword_item uitags[ui_last - 1] = {
1011 { "include", ui_include },
1012 { "menuset", ui_menuset },
1013 { "toolbar", ui_toolbar },
1014 { "toolbars", ui_toolbars }
1017 // Ensure that a file is read only once (prevents include loops)
1018 static std::list<string> uifiles;
1019 std::list<string>::const_iterator it = uifiles.begin();
1020 std::list<string>::const_iterator end = uifiles.end();
1021 it = std::find(it, end, name);
1023 lyxerr[Debug::INIT] << "UI file '" << name
1024 << "' has been read already. "
1025 << "Is this an include loop?"
1030 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1032 string const ui_path = libFileSearch("ui", name, "ui");
1034 if (ui_path.empty()) {
1035 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1036 showFileError(name);
1039 uifiles.push_back(name);
1041 lyxerr[Debug::INIT] << "Found " << name
1042 << " in " << ui_path << endl;
1043 LyXLex lex(uitags, ui_last - 1);
1044 lex.setFile(ui_path);
1046 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1050 if (lyxerr.debugging(Debug::PARSER))
1051 lex.printTable(lyxerr);
1053 while (lex.isOK()) {
1054 switch (lex.lex()) {
1057 string const file = lex.getString();
1058 if (!readUIFile(file))
1063 menubackend.read(lex);
1067 toolbarbackend.read(lex);
1071 toolbarbackend.readToolbars(lex);
1075 if (!rtrim(lex.getString()).empty())
1076 lex.printError("LyX::ReadUIFile: "
1077 "Unknown menu tag: `$$Token'");
1085 // Read the languages file `name'
1086 bool LyX::readLanguagesFile(string const & name)
1088 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1090 string const lang_path = libFileSearch(string(), name);
1091 if (lang_path.empty()) {
1092 showFileError(name);
1095 languages.read(lang_path);
1100 // Read the encodings file `name'
1101 bool LyX::readEncodingsFile(string const & name)
1103 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1105 string const enc_path = libFileSearch(string(), name);
1106 if (enc_path.empty()) {
1107 showFileError(name);
1110 encodings.read(enc_path);
1119 /// return the the number of arguments consumed
1120 typedef boost::function<int(string const &, string const &)> cmd_helper;
1122 int parse_dbg(string const & arg, string const &)
1125 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1126 Debug::showTags(lyxerr);
1129 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1131 lyxerr.level(Debug::value(arg));
1132 Debug::showLevel(lyxerr, lyxerr.level());
1137 int parse_help(string const &, string const &)
1140 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1141 "Command line switches (case sensitive):\n"
1142 "\t-help summarize LyX usage\n"
1143 "\t-userdir dir set user directory to dir\n"
1144 "\t-sysdir dir set system directory to dir\n"
1145 "\t-geometry WxH+X+Y set geometry of the main window\n"
1146 "\t-dbg feature[,feature]...\n"
1147 " select the features to debug.\n"
1148 " Type `lyx -dbg' to see the list of features\n"
1149 "\t-x [--execute] command\n"
1150 " where command is a lyx command.\n"
1151 "\t-e [--export] fmt\n"
1152 " where fmt is the export format of choice.\n"
1153 "\t-i [--import] fmt file.xxx\n"
1154 " where fmt is the import format of choice\n"
1155 " and file.xxx is the file to be imported.\n"
1156 "\t-version summarize version and build info\n"
1157 "Check the LyX man page for more details.")) << endl;
1162 int parse_version(string const &, string const &)
1164 lyxerr << "LyX " << lyx_version
1165 << " (" << lyx_release_date << ")" << endl;
1166 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1168 lyxerr << lyx_version_info << endl;
1173 int parse_sysdir(string const & arg, string const &)
1176 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1179 cl_system_support = arg;
1183 int parse_userdir(string const & arg, string const &)
1186 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1189 cl_user_support = arg;
1193 int parse_execute(string const & arg, string const &)
1196 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1203 int parse_export(string const & type, string const &)
1206 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1207 "--export switch")) << endl;
1210 batch = "buffer-export " + type;
1215 int parse_import(string const & type, string const & file)
1218 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1219 "--import switch")) << endl;
1223 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1227 batch = "buffer-import " + type + ' ' + file;
1234 void LyX::easyParse(int & argc, char * argv[])
1236 std::map<string, cmd_helper> cmdmap;
1238 cmdmap["-dbg"] = parse_dbg;
1239 cmdmap["-help"] = parse_help;
1240 cmdmap["--help"] = parse_help;
1241 cmdmap["-version"] = parse_version;
1242 cmdmap["--version"] = parse_version;
1243 cmdmap["-sysdir"] = parse_sysdir;
1244 cmdmap["-userdir"] = parse_userdir;
1245 cmdmap["-x"] = parse_execute;
1246 cmdmap["--execute"] = parse_execute;
1247 cmdmap["-e"] = parse_export;
1248 cmdmap["--export"] = parse_export;
1249 cmdmap["-i"] = parse_import;
1250 cmdmap["--import"] = parse_import;
1252 for (int i = 1; i < argc; ++i) {
1253 std::map<string, cmd_helper>::const_iterator it
1254 = cmdmap.find(argv[i]);
1256 // check for X11 -geometry option
1257 if (support::compare(argv[i], "-geometry") == 0)
1258 geometryOption_ = true;
1260 // don't complain if not found - may be parsed later
1261 if (it == cmdmap.end())
1264 string arg((i + 1 < argc) ? argv[i + 1] : "");
1265 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1267 int const remove = 1 + it->second(arg, arg2);
1269 // Now, remove used arguments by shifting
1270 // the following ones remove places down.
1272 for (int j = i; j < argc; ++j)
1273 argv[j] = argv[j + remove];
1277 batch_command = batch;
1281 FuncStatus getStatus(FuncRequest const & action)
1283 return LyX::ref().lyxFunc().getStatus(action);
1287 void dispatch(FuncRequest const & action)
1289 LyX::ref().lyxFunc().dispatch(action);
1293 BufferList & theBufferList()
1295 return LyX::ref().bufferList();
1299 LyXFunc & theLyXFunc()
1301 return LyX::ref().lyxFunc();
1305 LyXServer & theLyXServer()
1307 // FIXME: this should not be use_gui dependent
1308 BOOST_ASSERT(use_gui);
1309 return LyX::ref().server();
1313 LyXServerSocket & theLyXServerSocket()
1315 // FIXME: this should not be use_gui dependent
1316 BOOST_ASSERT(use_gui);
1317 return LyX::ref().socket();
1321 kb_keymap & theTopLevelKeymap()
1323 BOOST_ASSERT(use_gui);
1324 return LyX::ref().topLevelKeymap();
1328 IconvProcessor & utf8ToUcs4()
1330 return LyX::ref().iconvProcessor();