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"
61 #include <boost/bind.hpp>
62 #include <boost/filesystem/operations.hpp>
70 using support::addName;
71 using support::addPath;
72 using support::bformat;
73 using support::createDirectory;
74 using support::createLyXTmpDir;
75 using support::destroyDir;
76 using support::fileSearch;
77 using support::getEnv;
78 using support::i18nLibFileSearch;
79 using support::libFileSearch;
80 using support::package;
81 using support::prependEnvPath;
83 using support::Systemcall;
85 namespace Alert = frontend::Alert;
86 namespace os = support::os;
87 namespace fs = boost::filesystem;
94 #ifndef CXX_GLOBAL_CSTD
101 frontend::Application * theApp = 0;
103 /// are we using the GUI at all?
105 * We default to true and this is changed to false when the export feature is used.
112 // Filled with the command line arguments "foo" of "-sysdir foo" or
114 string cl_system_support;
115 string cl_user_support;
118 void showFileError(string const & error)
120 Alert::warning(_("Could not read configuration file"),
121 bformat(_("Error while reading the configuration file\n%1$s.\n"
122 "Please check your installation."), from_utf8(error)));
126 void reconfigureUserLyXDir()
128 string const configure_command = package().configure_command();
130 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
131 support::Path p(package().user_support());
133 one.startscript(Systemcall::Wait, configure_command);
134 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
140 /// The main application class private implementation.
141 struct LyX::Singletons
143 /// our function handler
146 BufferList buffer_list_;
148 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
150 boost::scoped_ptr<LyXServer> lyx_server_;
152 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
154 boost::scoped_ptr<frontend::Application> application_;
155 /// lyx session, containing lastfiles, lastfilepos, and lastopened
156 boost::scoped_ptr<Session> session_;
160 boost::scoped_ptr<LyX> LyX::singleton_;
163 int LyX::exec(int & argc, char * argv[])
165 BOOST_ASSERT(!singleton_.get());
166 // We must return from this before launching the gui so that
167 // other parts of the code can access singleton_ through
168 // LyX::ref and LyX::cref.
169 singleton_.reset(new LyX);
170 // Start the real execution loop.
171 return singleton_->priv_exec(argc, argv);
177 BOOST_ASSERT(singleton_.get());
178 return *singleton_.get();
182 LyX const & LyX::cref()
184 BOOST_ASSERT(singleton_.get());
185 return *singleton_.get();
190 : first_start(false), geometryOption_(false)
192 pimpl_.reset(new Singletons);
196 BufferList & LyX::bufferList()
198 return pimpl_->buffer_list_;
202 BufferList const & LyX::bufferList() const
204 return pimpl_->buffer_list_;
208 Session & LyX::session()
210 BOOST_ASSERT(pimpl_->session_.get());
211 return *pimpl_->session_.get();
215 Session const & LyX::session() const
217 BOOST_ASSERT(pimpl_->session_.get());
218 return *pimpl_->session_.get();
222 LyXFunc & LyX::lyxFunc()
224 return pimpl_->lyxfunc_;
228 LyXFunc const & LyX::lyxFunc() const
230 return pimpl_->lyxfunc_;
234 LyXServer & LyX::server()
236 BOOST_ASSERT(pimpl_->lyx_server_.get());
237 return *pimpl_->lyx_server_.get();
241 LyXServer const & LyX::server() const
243 BOOST_ASSERT(pimpl_->lyx_server_.get());
244 return *pimpl_->lyx_server_.get();
248 LyXServerSocket & LyX::socket()
250 BOOST_ASSERT(pimpl_->lyx_socket_.get());
251 return *pimpl_->lyx_socket_.get();
255 LyXServerSocket const & LyX::socket() const
257 BOOST_ASSERT(pimpl_->lyx_socket_.get());
258 return *pimpl_->lyx_socket_.get();
262 frontend::Application & LyX::application()
264 BOOST_ASSERT(pimpl_->application_.get());
265 return *pimpl_->application_.get();
269 frontend::Application const & LyX::application() const
271 BOOST_ASSERT(pimpl_->application_.get());
272 return *pimpl_->application_.get();
276 kb_keymap & LyX::topLevelKeymap()
278 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
279 return *pimpl_->toplevel_keymap_.get();
283 kb_keymap const & LyX::topLevelKeymap() const
285 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
286 return *pimpl_->toplevel_keymap_.get();
290 Buffer const * const LyX::updateInset(InsetBase const * inset) const
295 Buffer const * buffer_ptr = 0;
296 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
297 vector<int>::const_iterator it = view_ids.begin();
298 vector<int>::const_iterator const end = view_ids.end();
299 for (; it != end; ++it) {
301 pimpl_->application_->gui().view(*it).updateInset(inset);
309 int LyX::priv_exec(int & argc, char * argv[])
311 // Here we need to parse the command line. At least
312 // we need to parse for "-dbg" and "-help"
313 easyParse(argc, argv);
315 support::init_package(argv[0], cl_system_support, cl_user_support,
316 support::top_build_dir_is_one_level_up);
318 vector<string> files;
319 int exit_status = execBatchCommands(argc, argv, files);
325 // Force adding of font path _before_ Application is initialized
326 support::addFontResources();
327 pimpl_->application_.reset(createApplication(argc, argv));
329 // FIXME: this global pointer should probably go.
330 theApp = pimpl_->application_.get();
331 restoreGuiSession(files);
332 // Start the real execution loop.
335 /* Create a CoreApplication class that will provide the main event loop
336 * and the socket callback registering. With Qt4, only QtCore
337 * library would be needed.
338 * When this is 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.
399 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
404 pimpl_->session_->writeFile();
405 pimpl_->lyx_server_.reset();
406 pimpl_->lyx_socket_.reset();
407 pimpl_->application_->exit(0);
412 int LyX::execBatchCommands(int & argc, char * argv[],
413 vector<string> & files)
415 // check for any spurious extra arguments
416 // other than documents
417 for (int argi = 1; argi < argc ; ++argi) {
418 if (argv[argi][0] == '-') {
420 bformat(_("Wrong command line option `%1$s'. Exiting."),
421 from_utf8(argv[argi]))) << endl;
426 // Initialization of LyX (reads lyxrc and more)
427 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
428 bool success = init();
429 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
433 for (int argi = argc - 1; argi >= 1; --argi)
434 files.push_back(os::internal_path(argv[argi]));
437 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
439 // Execute batch commands if available
440 if (!batch_command.empty()) {
442 lyxerr[Debug::INIT] << "About to handle -x '"
443 << batch_command << '\'' << endl;
445 Buffer * last_loaded = 0;
447 vector<string>::const_iterator it = files.begin();
448 vector<string>::const_iterator end = files.end();
450 for (; it != end; ++it) {
451 // get absolute path of file and add ".lyx" to
452 // the filename if necessary
453 string s = fileSearch(string(), *it, "lyx");
455 Buffer * const b = newFile(*it, string(), true);
459 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
460 if (loadLyXFile(buf, s)) {
462 ErrorList const & el = buf->errorList("Parse");
464 for_each(el.begin(), el.end(),
465 boost::bind(&LyX::printError, this, _1));
468 pimpl_->buffer_list_.release(buf);
472 // try to dispatch to last loaded buffer first
475 if (last_loaded->dispatch(batch_command, &success)) {
480 files.clear(); // the files are already loaded
487 void LyX::restoreGuiSession(vector<string> const & files)
489 LyXView * view = newLyXView();
492 for_each(files.begin(), files.end(),
493 bind(&LyXView::loadLyXFile, view, _1, true));
495 // if a file is specified, I assume that user wants to edit *that* file
496 if (files.empty() && lyxrc.load_session) {
497 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
498 // do not add to the lastfile list since these files are restored from
499 // last seesion, and should be already there (regular files), or should
500 // not be added at all (help files).
501 for_each(lastopened.begin(), lastopened.end(),
502 bind(&LyXView::loadLyXFile, view, _1, false));
504 // clear this list to save a few bytes of RAM
505 pimpl_->session_->lastOpened().clear();
509 LyXView * LyX::newLyXView()
514 // determine windows size and position, from lyxrc and/or session
516 unsigned int width = 690;
517 unsigned int height = 510;
518 bool maximize = false;
520 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
521 width = lyxrc.geometry_width;
522 height = lyxrc.geometry_height;
524 // if lyxrc returns (0,0), then use session info
526 string val = session().sessionInfo().load("WindowWidth");
528 width = convert<unsigned int>(val);
529 val = session().sessionInfo().load("WindowHeight");
531 height = convert<unsigned int>(val);
532 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
536 // if user wants to restore window position
539 if (lyxrc.geometry_xysaved) {
540 string val = session().sessionInfo().load("WindowPosX");
542 posx = convert<int>(val);
543 val = session().sessionInfo().load("WindowPosY");
545 posy = convert<int>(val);
548 if (geometryOption_) {
552 // create the main window
553 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
561 The SIGHUP signal does not exist on Windows and does not need to be handled.
563 Windows handles SIGFPE and SIGSEGV signals as expected.
565 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
566 cause a new thread to be spawned. This may well result in unexpected
567 behaviour by the single-threaded LyX.
569 SIGTERM signals will come only from another process actually sending
570 that signal using 'raise' in Windows' POSIX compatability layer. It will
571 not come from the general "terminate process" methods that everyone
572 actually uses (and which can't be trapped). Killing an app 'politely' on
573 Windows involves first sending a WM_CLOSE message, something that is
574 caught already by the Qt frontend.
576 For more information see:
578 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
579 ...signals are mostly useless on Windows for a variety of reasons that are
582 'UNIX Application Migration Guide, Chapter 9'
583 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
585 'How To Terminate an Application "Cleanly" in Win32'
586 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
590 static void error_handler(int err_sig)
592 // Throw away any signals other than the first one received.
593 static sig_atomic_t handling_error = false;
596 handling_error = true;
598 // We have received a signal indicating a fatal error, so
599 // try and save the data ASAP.
600 LyX::cref().emergencyCleanup();
602 // These lyxerr calls may or may not work:
604 // Signals are asynchronous, so the main program may be in a very
605 // fragile state when a signal is processed and thus while a signal
606 // handler function executes.
607 // In general, therefore, we should avoid performing any
608 // I/O operations or calling most library and system functions from
611 // This shouldn't matter here, however, as we've already invoked
616 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
620 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
623 lyxerr << "\nlyx: SIGSEGV signal caught\n"
624 "Sorry, you have found a bug in LyX. "
625 "Please read the bug-reporting instructions "
626 "in Help->Introduction and send us a bug report, "
627 "if necessary. Thanks !\nBye." << endl;
635 // Deinstall the signal handlers
637 signal(SIGHUP, SIG_DFL);
639 signal(SIGINT, SIG_DFL);
640 signal(SIGFPE, SIG_DFL);
641 signal(SIGSEGV, SIG_DFL);
642 signal(SIGTERM, SIG_DFL);
645 if (err_sig == SIGSEGV ||
646 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
648 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
657 void LyX::printError(ErrorItem const & ei)
659 docstring tmp = _("LyX: ") + ei.error + char_type(':')
661 std::cerr << to_utf8(tmp) << std::endl;
665 void LyX::initGuiFont()
667 if (lyxrc.roman_font_name.empty())
668 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
670 if (lyxrc.sans_font_name.empty())
671 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
673 if (lyxrc.typewriter_font_name.empty())
674 lyxrc.typewriter_font_name
675 = pimpl_->application_->typewriterFontName();
682 signal(SIGHUP, error_handler);
684 signal(SIGFPE, error_handler);
685 signal(SIGSEGV, error_handler);
686 signal(SIGINT, error_handler);
687 signal(SIGTERM, error_handler);
688 // SIGPIPE can be safely ignored.
690 lyxrc.tempdir_path = package().temp_dir();
691 lyxrc.document_path = package().document_dir();
693 if (lyxrc.template_path.empty()) {
694 lyxrc.template_path = addPath(package().system_support(),
699 // Read configuration files
702 // This one may have been distributed along with LyX.
703 if (!readRcFile("lyxrc.dist"))
706 // Set the PATH correctly.
707 #if !defined (USE_POSIX_PACKAGING)
708 // Add the directory containing the LyX executable to the path
709 // so that LyX can find things like tex2lyx.
710 if (package().build_support().empty())
711 prependEnvPath("PATH", package().binary_dir());
713 if (!lyxrc.path_prefix.empty())
714 prependEnvPath("PATH", lyxrc.path_prefix);
716 // Check that user LyX directory is ok.
717 if (queryUserLyXDir(package().explicit_user_support()))
718 reconfigureUserLyXDir();
720 // no need for a splash when there is no GUI
725 // This one is generated in user_support directory by lib/configure.py.
726 if (!readRcFile("lyxrc.defaults"))
729 // Query the OS to know what formats are viewed natively
730 formats.setAutoOpen();
732 // Read lyxrc.dist again to be able to override viewer auto-detection.
733 readRcFile("lyxrc.dist");
735 system_lyxrc = lyxrc;
736 system_formats = formats;
737 system_converters = converters;
738 system_movers = movers;
739 system_lcolor = lcolor;
741 // This one is edited through the preferences dialog.
742 if (!readRcFile("preferences"))
745 if (!readEncodingsFile("encodings"))
747 if (!readLanguagesFile("languages"))
751 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
757 pimpl_->toplevel_keymap_.reset(new kb_keymap);
758 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
759 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
761 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
764 if (!readUIFile(lyxrc.ui_file))
768 if (lyxerr.debugging(Debug::LYXRC))
771 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
772 if (!lyxrc.path_prefix.empty())
773 prependEnvPath("PATH", lyxrc.path_prefix);
775 if (fs::exists(lyxrc.document_path) &&
776 fs::is_directory(lyxrc.document_path))
777 package().document_dir() = lyxrc.document_path;
779 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
780 if (package().temp_dir().empty()) {
781 Alert::error(_("Could not create temporary directory"),
782 bformat(_("Could not create a temporary directory in\n"
783 "%1$s. Make sure that this\n"
784 "path exists and is writable and try again."),
785 from_utf8(lyxrc.tempdir_path)));
786 // createLyXTmpDir() tries sufficiently hard to create a
787 // usable temp dir, so the probability to come here is
788 // close to zero. We therefore don't try to overcome this
789 // problem with e.g. asking the user for a new path and
790 // trying again but simply exit.
794 if (lyxerr.debugging(Debug::INIT)) {
795 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
798 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
799 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
801 // This must happen after package initialization and after lyxrc is
802 // read, therefore it can't be done by a static object.
803 ConverterCache::init();
809 void LyX::defaultKeyBindings(kb_keymap * kbmap)
811 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
812 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
813 kbmap->bind("Up", FuncRequest(LFUN_UP));
814 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
816 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
817 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
818 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
819 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
821 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
822 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
823 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
824 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
826 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
827 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
829 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
830 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
832 // kbmap->bindings to enable the use of the numeric keypad
834 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
835 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
836 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
837 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
838 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
839 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
840 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
841 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
842 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
843 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
844 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
845 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
846 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
847 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
848 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
849 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
850 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
851 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
852 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
853 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
854 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
855 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
856 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
857 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
861 void LyX::emergencyCleanup() const
863 // what to do about tmpfiles is non-obvious. we would
864 // like to delete any we find, but our lyxdir might
865 // contain documents etc. which might be helpful on
868 pimpl_->buffer_list_.emergencyWriteAll();
870 pimpl_->lyx_server_->emergencyCleanup();
871 pimpl_->lyx_server_.reset();
872 pimpl_->lyx_socket_.reset();
877 void LyX::deadKeyBindings(kb_keymap * kbmap)
879 // bindKeyings for transparent handling of deadkeys
880 // The keysyms are gotten from XFree86 X11R6
881 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
882 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
883 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
884 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
885 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
886 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
887 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
888 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
889 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
890 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
891 // nothing with this name
892 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
893 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
894 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
895 // nothing with this name either...
896 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
897 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
898 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
899 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
905 // return true if file does not exist or is older than configure.py.
906 bool needsUpdate(string const & file)
908 static string const configure_script =
909 addName(package().system_support(), "configure.py");
910 string const absfile =
911 addName(package().user_support(), file);
913 return (! fs::exists(absfile))
914 || (fs::last_write_time(configure_script)
915 > fs::last_write_time(absfile));
921 bool LyX::queryUserLyXDir(bool explicit_userdir)
923 // Does user directory exist?
924 if (fs::exists(package().user_support()) &&
925 fs::is_directory(package().user_support())) {
928 return needsUpdate("lyxrc.defaults")
929 || needsUpdate("textclass.lst")
930 || needsUpdate("packages.lst");
933 first_start = !explicit_userdir;
935 // If the user specified explicitly a directory, ask whether
936 // to create it. If the user says "no", then exit.
937 if (explicit_userdir &&
939 _("Missing user LyX directory"),
940 bformat(_("You have specified a non-existent user "
941 "LyX directory, %1$s.\n"
942 "It is needed to keep your own configuration."),
943 from_utf8(package().user_support())),
945 _("&Create directory"),
947 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
948 earlyExit(EXIT_FAILURE);
951 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
952 from_utf8(package().user_support())))
955 if (!createDirectory(package().user_support(), 0755)) {
956 // Failed, so let's exit.
957 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
959 earlyExit(EXIT_FAILURE);
966 bool LyX::readRcFile(string const & name)
968 lyxerr[Debug::INIT] << "About to read " << name << "... ";
970 string const lyxrc_path = libFileSearch(string(), name);
971 if (!lyxrc_path.empty()) {
973 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
975 if (lyxrc.read(lyxrc_path) < 0) {
980 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
986 // Read the ui file `name'
987 bool LyX::readUIFile(string const & name)
997 struct keyword_item uitags[ui_last - 1] = {
998 { "include", ui_include },
999 { "menuset", ui_menuset },
1000 { "toolbar", ui_toolbar },
1001 { "toolbars", ui_toolbars }
1004 // Ensure that a file is read only once (prevents include loops)
1005 static std::list<string> uifiles;
1006 std::list<string>::const_iterator it = uifiles.begin();
1007 std::list<string>::const_iterator end = uifiles.end();
1008 it = std::find(it, end, name);
1010 lyxerr[Debug::INIT] << "UI file '" << name
1011 << "' has been read already. "
1012 << "Is this an include loop?"
1017 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1019 string const ui_path = libFileSearch("ui", name, "ui");
1021 if (ui_path.empty()) {
1022 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1023 showFileError(name);
1026 uifiles.push_back(name);
1028 lyxerr[Debug::INIT] << "Found " << name
1029 << " in " << ui_path << endl;
1030 LyXLex lex(uitags, ui_last - 1);
1031 lex.setFile(ui_path);
1033 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1037 if (lyxerr.debugging(Debug::PARSER))
1038 lex.printTable(lyxerr);
1040 while (lex.isOK()) {
1041 switch (lex.lex()) {
1044 string const file = lex.getString();
1045 if (!readUIFile(file))
1050 menubackend.read(lex);
1054 toolbarbackend.read(lex);
1058 toolbarbackend.readToolbars(lex);
1062 if (!rtrim(lex.getString()).empty())
1063 lex.printError("LyX::ReadUIFile: "
1064 "Unknown menu tag: `$$Token'");
1072 // Read the languages file `name'
1073 bool LyX::readLanguagesFile(string const & name)
1075 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1077 string const lang_path = libFileSearch(string(), name);
1078 if (lang_path.empty()) {
1079 showFileError(name);
1082 languages.read(lang_path);
1087 // Read the encodings file `name'
1088 bool LyX::readEncodingsFile(string const & name)
1090 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1092 string const enc_path = libFileSearch(string(), name);
1093 if (enc_path.empty()) {
1094 showFileError(name);
1097 encodings.read(enc_path);
1106 /// return the the number of arguments consumed
1107 typedef boost::function<int(string const &, string const &)> cmd_helper;
1109 int parse_dbg(string const & arg, string const &)
1112 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1113 Debug::showTags(lyxerr);
1116 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1118 lyxerr.level(Debug::value(arg));
1119 Debug::showLevel(lyxerr, lyxerr.level());
1124 int parse_help(string const &, string const &)
1127 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1128 "Command line switches (case sensitive):\n"
1129 "\t-help summarize LyX usage\n"
1130 "\t-userdir dir set user directory to dir\n"
1131 "\t-sysdir dir set system directory to dir\n"
1132 "\t-geometry WxH+X+Y set geometry of the main window\n"
1133 "\t-dbg feature[,feature]...\n"
1134 " select the features to debug.\n"
1135 " Type `lyx -dbg' to see the list of features\n"
1136 "\t-x [--execute] command\n"
1137 " where command is a lyx command.\n"
1138 "\t-e [--export] fmt\n"
1139 " where fmt is the export format of choice.\n"
1140 "\t-i [--import] fmt file.xxx\n"
1141 " where fmt is the import format of choice\n"
1142 " and file.xxx is the file to be imported.\n"
1143 "\t-version summarize version and build info\n"
1144 "Check the LyX man page for more details.")) << endl;
1149 int parse_version(string const &, string const &)
1151 lyxerr << "LyX " << lyx_version
1152 << " (" << lyx_release_date << ")" << endl;
1153 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1155 lyxerr << lyx_version_info << endl;
1160 int parse_sysdir(string const & arg, string const &)
1163 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1166 cl_system_support = arg;
1170 int parse_userdir(string const & arg, string const &)
1173 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1176 cl_user_support = arg;
1180 int parse_execute(string const & arg, string const &)
1183 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1190 int parse_export(string const & type, string const &)
1193 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1194 "--export switch")) << endl;
1197 batch = "buffer-export " + type;
1202 int parse_import(string const & type, string const & file)
1205 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1206 "--import switch")) << endl;
1210 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1214 batch = "buffer-import " + type + ' ' + file;
1221 void LyX::easyParse(int & argc, char * argv[])
1223 std::map<string, cmd_helper> cmdmap;
1225 cmdmap["-dbg"] = parse_dbg;
1226 cmdmap["-help"] = parse_help;
1227 cmdmap["--help"] = parse_help;
1228 cmdmap["-version"] = parse_version;
1229 cmdmap["--version"] = parse_version;
1230 cmdmap["-sysdir"] = parse_sysdir;
1231 cmdmap["-userdir"] = parse_userdir;
1232 cmdmap["-x"] = parse_execute;
1233 cmdmap["--execute"] = parse_execute;
1234 cmdmap["-e"] = parse_export;
1235 cmdmap["--export"] = parse_export;
1236 cmdmap["-i"] = parse_import;
1237 cmdmap["--import"] = parse_import;
1239 for (int i = 1; i < argc; ++i) {
1240 std::map<string, cmd_helper>::const_iterator it
1241 = cmdmap.find(argv[i]);
1243 // check for X11 -geometry option
1244 if (support::compare(argv[i], "-geometry") == 0)
1245 geometryOption_ = true;
1247 // don't complain if not found - may be parsed later
1248 if (it == cmdmap.end())
1251 string arg((i + 1 < argc) ? argv[i + 1] : "");
1252 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1254 int const remove = 1 + it->second(arg, arg2);
1256 // Now, remove used arguments by shifting
1257 // the following ones remove places down.
1259 for (int j = i; j < argc; ++j)
1260 argv[j] = argv[j + remove];
1264 batch_command = batch;
1268 FuncStatus getStatus(FuncRequest const & action)
1270 return LyX::ref().lyxFunc().getStatus(action);
1274 void dispatch(FuncRequest const & action)
1276 LyX::ref().lyxFunc().dispatch(action);
1280 BufferList & theBufferList()
1282 return LyX::ref().bufferList();
1286 LyXFunc & theLyXFunc()
1288 return LyX::ref().lyxFunc();
1292 LyXServer & theLyXServer()
1294 // FIXME: this should not be use_gui dependent
1295 BOOST_ASSERT(use_gui);
1296 return LyX::ref().server();
1300 LyXServerSocket & theLyXServerSocket()
1302 // FIXME: this should not be use_gui dependent
1303 BOOST_ASSERT(use_gui);
1304 return LyX::ref().socket();
1308 kb_keymap & theTopLevelKeymap()
1310 BOOST_ASSERT(use_gui);
1311 return LyX::ref().topLevelKeymap();