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_;
164 // Static data are not treated in the same way at all on the Mac (and
165 // the LyX singleton has static methods). This is the reason why the
166 // exit command on the Mac bypasses our dispatch machinery altogether.
167 // On Linux and Windows we won't pass a second time through quit()
168 // because quitting will already be set to true.
174 int LyX::exec(int & argc, char * argv[])
176 BOOST_ASSERT(!singleton_.get());
177 // We must return from this before launching the gui so that
178 // other parts of the code can access singleton_ through
179 // LyX::ref and LyX::cref.
180 singleton_.reset(new LyX);
181 // Start the real execution loop.
182 return singleton_->priv_exec(argc, argv);
188 BOOST_ASSERT(singleton_.get());
189 return *singleton_.get();
193 LyX const & LyX::cref()
195 BOOST_ASSERT(singleton_.get());
196 return *singleton_.get();
201 : first_start(false), geometryOption_(false)
203 pimpl_.reset(new Singletons);
207 BufferList & LyX::bufferList()
209 return pimpl_->buffer_list_;
213 BufferList const & LyX::bufferList() const
215 return pimpl_->buffer_list_;
219 Session & LyX::session()
221 BOOST_ASSERT(pimpl_->session_.get());
222 return *pimpl_->session_.get();
226 Session const & LyX::session() const
228 BOOST_ASSERT(pimpl_->session_.get());
229 return *pimpl_->session_.get();
233 LyXFunc & LyX::lyxFunc()
235 return pimpl_->lyxfunc_;
239 LyXFunc const & LyX::lyxFunc() const
241 return pimpl_->lyxfunc_;
245 LyXServer & LyX::server()
247 BOOST_ASSERT(pimpl_->lyx_server_.get());
248 return *pimpl_->lyx_server_.get();
252 LyXServer const & LyX::server() const
254 BOOST_ASSERT(pimpl_->lyx_server_.get());
255 return *pimpl_->lyx_server_.get();
259 LyXServerSocket & LyX::socket()
261 BOOST_ASSERT(pimpl_->lyx_socket_.get());
262 return *pimpl_->lyx_socket_.get();
266 LyXServerSocket const & LyX::socket() const
268 BOOST_ASSERT(pimpl_->lyx_socket_.get());
269 return *pimpl_->lyx_socket_.get();
273 frontend::Application & LyX::application()
275 BOOST_ASSERT(pimpl_->application_.get());
276 return *pimpl_->application_.get();
280 frontend::Application const & LyX::application() const
282 BOOST_ASSERT(pimpl_->application_.get());
283 return *pimpl_->application_.get();
287 kb_keymap & LyX::topLevelKeymap()
289 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
290 return *pimpl_->toplevel_keymap_.get();
294 kb_keymap const & LyX::topLevelKeymap() const
296 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
297 return *pimpl_->toplevel_keymap_.get();
301 Buffer const * const LyX::updateInset(InsetBase const * inset) const
306 Buffer const * buffer_ptr = 0;
307 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
308 vector<int>::const_iterator it = view_ids.begin();
309 vector<int>::const_iterator const end = view_ids.end();
310 for (; it != end; ++it) {
312 pimpl_->application_->gui().view(*it).updateInset(inset);
320 int LyX::priv_exec(int & argc, char * argv[])
322 // Here we need to parse the command line. At least
323 // we need to parse for "-dbg" and "-help"
324 easyParse(argc, argv);
326 support::init_package(argv[0], cl_system_support, cl_user_support,
327 support::top_build_dir_is_one_level_up);
329 vector<string> files;
330 int exit_status = execBatchCommands(argc, argv, files);
336 // Force adding of font path _before_ Application is initialized
337 support::addFontResources();
338 pimpl_->application_.reset(createApplication(argc, argv));
340 // FIXME: this global pointer should probably go.
341 theApp = pimpl_->application_.get();
342 restoreGuiSession(files);
343 // Start the real execution loop.
346 /* Create a CoreApplication class that will provide the main event loop
347 * and the socket callback registering. With Qt4, only QtCore
348 * library would be needed.
349 * When this is done, a server_mode could be created and the following two
350 * line would be moved out from here.
352 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
353 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
354 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
356 // handle the batch commands the user asked for
357 if (!batch_command.empty()) {
358 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
361 exit_status = pimpl_->application_->start(batch_command);
362 // Kill the application object before exiting. This avoid crash
364 pimpl_->application_.reset();
365 // Restore original font resources after Application is destroyed.
366 support::restoreFontResources();
369 // FIXME: create a ConsoleApplication
377 void LyX::prepareExit()
379 // Set a flag that we do quitting from the program,
380 // so no refreshes are necessary.
383 // close buffers first
384 pimpl_->buffer_list_.closeAll();
386 // do any other cleanup procedures now
387 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
389 if (!destroyDir(package().temp_dir())) {
390 // FIXME UNICODE: package().temp_dir() could in theory contain utf8 characters.
391 // We cannot use from_utf8() here because this involves the use of static data
392 // that may have been destroyed already on Mac systems.
393 docstring const msg =
394 bformat(_("Unable to remove the temporary directory %1$s"),
395 from_ascii(package().temp_dir()));
396 Alert::warning(_("Unable to remove temporary directory"), msg);
401 void LyX::earlyExit(int status)
403 BOOST_ASSERT(pimpl_->application_.get());
404 // LyX::pimpl_::application_ is not initialised at this
405 // point so it's safe to just exit after some cleanup.
413 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
418 pimpl_->session_->writeFile();
419 pimpl_->lyx_server_.reset();
420 pimpl_->lyx_socket_.reset();
421 pimpl_->application_->exit(0);
426 int LyX::execBatchCommands(int & argc, char * argv[],
427 vector<string> & files)
429 // check for any spurious extra arguments
430 // other than documents
431 for (int argi = 1; argi < argc ; ++argi) {
432 if (argv[argi][0] == '-') {
434 bformat(_("Wrong command line option `%1$s'. Exiting."),
435 from_utf8(argv[argi]))) << endl;
440 // Initialization of LyX (reads lyxrc and more)
441 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
442 bool success = init();
443 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
447 for (int argi = argc - 1; argi >= 1; --argi)
448 files.push_back(os::internal_path(argv[argi]));
451 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
453 // Execute batch commands if available
454 if (!batch_command.empty()) {
456 lyxerr[Debug::INIT] << "About to handle -x '"
457 << batch_command << '\'' << endl;
459 Buffer * last_loaded = 0;
461 vector<string>::const_iterator it = files.begin();
462 vector<string>::const_iterator end = files.end();
464 for (; it != end; ++it) {
465 // get absolute path of file and add ".lyx" to
466 // the filename if necessary
467 string s = fileSearch(string(), *it, "lyx");
469 Buffer * const b = newFile(*it, string(), true);
473 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
474 if (loadLyXFile(buf, s)) {
476 ErrorList const & el = buf->errorList("Parse");
478 for_each(el.begin(), el.end(),
479 boost::bind(&LyX::printError, this, _1));
482 pimpl_->buffer_list_.release(buf);
486 // try to dispatch to last loaded buffer first
489 if (last_loaded->dispatch(batch_command, &success)) {
494 files.clear(); // the files are already loaded
501 void LyX::restoreGuiSession(vector<string> const & files)
503 LyXView * view = newLyXView();
506 for_each(files.begin(), files.end(),
507 bind(&LyXView::loadLyXFile, view, _1, true));
509 // if a file is specified, I assume that user wants to edit *that* file
510 if (files.empty() && lyxrc.load_session) {
511 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
512 // do not add to the lastfile list since these files are restored from
513 // last seesion, and should be already there (regular files), or should
514 // not be added at all (help files).
515 for_each(lastopened.begin(), lastopened.end(),
516 bind(&LyXView::loadLyXFile, view, _1, false));
518 // clear this list to save a few bytes of RAM
519 pimpl_->session_->lastOpened().clear();
523 LyXView * LyX::newLyXView()
528 // determine windows size and position, from lyxrc and/or session
530 unsigned int width = 690;
531 unsigned int height = 510;
532 bool maximize = false;
534 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
535 width = lyxrc.geometry_width;
536 height = lyxrc.geometry_height;
538 // if lyxrc returns (0,0), then use session info
540 string val = session().sessionInfo().load("WindowWidth");
542 width = convert<unsigned int>(val);
543 val = session().sessionInfo().load("WindowHeight");
545 height = convert<unsigned int>(val);
546 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
550 // if user wants to restore window position
553 if (lyxrc.geometry_xysaved) {
554 string val = session().sessionInfo().load("WindowPosX");
556 posx = convert<int>(val);
557 val = session().sessionInfo().load("WindowPosY");
559 posy = convert<int>(val);
562 if (geometryOption_) {
566 // create the main window
567 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
575 The SIGHUP signal does not exist on Windows and does not need to be handled.
577 Windows handles SIGFPE and SIGSEGV signals as expected.
579 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
580 cause a new thread to be spawned. This may well result in unexpected
581 behaviour by the single-threaded LyX.
583 SIGTERM signals will come only from another process actually sending
584 that signal using 'raise' in Windows' POSIX compatability layer. It will
585 not come from the general "terminate process" methods that everyone
586 actually uses (and which can't be trapped). Killing an app 'politely' on
587 Windows involves first sending a WM_CLOSE message, something that is
588 caught already by the Qt frontend.
590 For more information see:
592 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
593 ...signals are mostly useless on Windows for a variety of reasons that are
596 'UNIX Application Migration Guide, Chapter 9'
597 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
599 'How To Terminate an Application "Cleanly" in Win32'
600 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
604 static void error_handler(int err_sig)
606 // Throw away any signals other than the first one received.
607 static sig_atomic_t handling_error = false;
610 handling_error = true;
612 // We have received a signal indicating a fatal error, so
613 // try and save the data ASAP.
614 LyX::cref().emergencyCleanup();
616 // These lyxerr calls may or may not work:
618 // Signals are asynchronous, so the main program may be in a very
619 // fragile state when a signal is processed and thus while a signal
620 // handler function executes.
621 // In general, therefore, we should avoid performing any
622 // I/O operations or calling most library and system functions from
625 // This shouldn't matter here, however, as we've already invoked
630 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
634 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
637 lyxerr << "\nlyx: SIGSEGV signal caught\n"
638 "Sorry, you have found a bug in LyX. "
639 "Please read the bug-reporting instructions "
640 "in Help->Introduction and send us a bug report, "
641 "if necessary. Thanks !\nBye." << endl;
649 // Deinstall the signal handlers
651 signal(SIGHUP, SIG_DFL);
653 signal(SIGINT, SIG_DFL);
654 signal(SIGFPE, SIG_DFL);
655 signal(SIGSEGV, SIG_DFL);
656 signal(SIGTERM, SIG_DFL);
659 if (err_sig == SIGSEGV ||
660 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
662 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
671 void LyX::printError(ErrorItem const & ei)
673 docstring tmp = _("LyX: ") + ei.error + char_type(':')
675 std::cerr << to_utf8(tmp) << std::endl;
679 void LyX::initGuiFont()
681 if (lyxrc.roman_font_name.empty())
682 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
684 if (lyxrc.sans_font_name.empty())
685 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
687 if (lyxrc.typewriter_font_name.empty())
688 lyxrc.typewriter_font_name
689 = pimpl_->application_->typewriterFontName();
696 signal(SIGHUP, error_handler);
698 signal(SIGFPE, error_handler);
699 signal(SIGSEGV, error_handler);
700 signal(SIGINT, error_handler);
701 signal(SIGTERM, error_handler);
702 // SIGPIPE can be safely ignored.
704 lyxrc.tempdir_path = package().temp_dir();
705 lyxrc.document_path = package().document_dir();
707 if (lyxrc.template_path.empty()) {
708 lyxrc.template_path = addPath(package().system_support(),
713 // Read configuration files
716 // This one may have been distributed along with LyX.
717 if (!readRcFile("lyxrc.dist"))
720 // Set the PATH correctly.
721 #if !defined (USE_POSIX_PACKAGING)
722 // Add the directory containing the LyX executable to the path
723 // so that LyX can find things like tex2lyx.
724 if (package().build_support().empty())
725 prependEnvPath("PATH", package().binary_dir());
727 if (!lyxrc.path_prefix.empty())
728 prependEnvPath("PATH", lyxrc.path_prefix);
730 // Check that user LyX directory is ok.
731 if (queryUserLyXDir(package().explicit_user_support()))
732 reconfigureUserLyXDir();
734 // no need for a splash when there is no GUI
739 // This one is generated in user_support directory by lib/configure.py.
740 if (!readRcFile("lyxrc.defaults"))
743 // Query the OS to know what formats are viewed natively
744 formats.setAutoOpen();
746 // Read lyxrc.dist again to be able to override viewer auto-detection.
747 readRcFile("lyxrc.dist");
749 system_lyxrc = lyxrc;
750 system_formats = formats;
751 system_converters = converters;
752 system_movers = movers;
753 system_lcolor = lcolor;
755 // This one is edited through the preferences dialog.
756 if (!readRcFile("preferences"))
759 if (!readEncodingsFile("encodings"))
761 if (!readLanguagesFile("languages"))
765 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
771 pimpl_->toplevel_keymap_.reset(new kb_keymap);
772 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
773 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
775 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
778 if (!readUIFile(lyxrc.ui_file))
782 if (lyxerr.debugging(Debug::LYXRC))
785 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
786 if (!lyxrc.path_prefix.empty())
787 prependEnvPath("PATH", lyxrc.path_prefix);
789 if (fs::exists(lyxrc.document_path) &&
790 fs::is_directory(lyxrc.document_path))
791 package().document_dir() = lyxrc.document_path;
793 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
794 if (package().temp_dir().empty()) {
795 Alert::error(_("Could not create temporary directory"),
796 bformat(_("Could not create a temporary directory in\n"
797 "%1$s. Make sure that this\n"
798 "path exists and is writable and try again."),
799 from_utf8(lyxrc.tempdir_path)));
800 // createLyXTmpDir() tries sufficiently hard to create a
801 // usable temp dir, so the probability to come here is
802 // close to zero. We therefore don't try to overcome this
803 // problem with e.g. asking the user for a new path and
804 // trying again but simply exit.
808 if (lyxerr.debugging(Debug::INIT)) {
809 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
812 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
813 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
815 // This must happen after package initialization and after lyxrc is
816 // read, therefore it can't be done by a static object.
817 ConverterCache::init();
823 void LyX::defaultKeyBindings(kb_keymap * kbmap)
825 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
826 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
827 kbmap->bind("Up", FuncRequest(LFUN_UP));
828 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
830 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
831 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
832 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
833 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
835 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
836 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
837 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
838 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
840 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
841 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
843 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
844 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
846 // kbmap->bindings to enable the use of the numeric keypad
848 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
849 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
850 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
851 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
852 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
853 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
854 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
855 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
856 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
857 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
858 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
859 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
860 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
861 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
862 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
863 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
864 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
865 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
866 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
867 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
868 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
869 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
870 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
871 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
875 void LyX::emergencyCleanup() const
877 // what to do about tmpfiles is non-obvious. we would
878 // like to delete any we find, but our lyxdir might
879 // contain documents etc. which might be helpful on
882 pimpl_->buffer_list_.emergencyWriteAll();
884 pimpl_->lyx_server_->emergencyCleanup();
885 pimpl_->lyx_server_.reset();
886 pimpl_->lyx_socket_.reset();
891 void LyX::deadKeyBindings(kb_keymap * kbmap)
893 // bindKeyings for transparent handling of deadkeys
894 // The keysyms are gotten from XFree86 X11R6
895 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
896 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
897 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
898 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
899 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
900 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
901 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
902 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
903 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
904 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
905 // nothing with this name
906 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
907 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
908 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
909 // nothing with this name either...
910 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
911 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
912 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
913 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
919 // return true if file does not exist or is older than configure.py.
920 bool needsUpdate(string const & file)
922 static string const configure_script =
923 addName(package().system_support(), "configure.py");
924 string const absfile =
925 addName(package().user_support(), file);
927 return (! fs::exists(absfile))
928 || (fs::last_write_time(configure_script)
929 > fs::last_write_time(absfile));
935 bool LyX::queryUserLyXDir(bool explicit_userdir)
937 // Does user directory exist?
938 if (fs::exists(package().user_support()) &&
939 fs::is_directory(package().user_support())) {
942 return needsUpdate("lyxrc.defaults")
943 || needsUpdate("textclass.lst")
944 || needsUpdate("packages.lst");
947 first_start = !explicit_userdir;
949 // If the user specified explicitly a directory, ask whether
950 // to create it. If the user says "no", then exit.
951 if (explicit_userdir &&
953 _("Missing user LyX directory"),
954 bformat(_("You have specified a non-existent user "
955 "LyX directory, %1$s.\n"
956 "It is needed to keep your own configuration."),
957 from_utf8(package().user_support())),
959 _("&Create directory"),
961 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
962 earlyExit(EXIT_FAILURE);
965 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
966 from_utf8(package().user_support())))
969 if (!createDirectory(package().user_support(), 0755)) {
970 // Failed, so let's exit.
971 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
973 earlyExit(EXIT_FAILURE);
980 bool LyX::readRcFile(string const & name)
982 lyxerr[Debug::INIT] << "About to read " << name << "... ";
984 string const lyxrc_path = libFileSearch(string(), name);
985 if (!lyxrc_path.empty()) {
987 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
989 if (lyxrc.read(lyxrc_path) < 0) {
994 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1000 // Read the ui file `name'
1001 bool LyX::readUIFile(string const & name)
1011 struct keyword_item uitags[ui_last - 1] = {
1012 { "include", ui_include },
1013 { "menuset", ui_menuset },
1014 { "toolbar", ui_toolbar },
1015 { "toolbars", ui_toolbars }
1018 // Ensure that a file is read only once (prevents include loops)
1019 static std::list<string> uifiles;
1020 std::list<string>::const_iterator it = uifiles.begin();
1021 std::list<string>::const_iterator end = uifiles.end();
1022 it = std::find(it, end, name);
1024 lyxerr[Debug::INIT] << "UI file '" << name
1025 << "' has been read already. "
1026 << "Is this an include loop?"
1031 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1033 string const ui_path = libFileSearch("ui", name, "ui");
1035 if (ui_path.empty()) {
1036 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1037 showFileError(name);
1040 uifiles.push_back(name);
1042 lyxerr[Debug::INIT] << "Found " << name
1043 << " in " << ui_path << endl;
1044 LyXLex lex(uitags, ui_last - 1);
1045 lex.setFile(ui_path);
1047 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1051 if (lyxerr.debugging(Debug::PARSER))
1052 lex.printTable(lyxerr);
1054 while (lex.isOK()) {
1055 switch (lex.lex()) {
1058 string const file = lex.getString();
1059 if (!readUIFile(file))
1064 menubackend.read(lex);
1068 toolbarbackend.read(lex);
1072 toolbarbackend.readToolbars(lex);
1076 if (!rtrim(lex.getString()).empty())
1077 lex.printError("LyX::ReadUIFile: "
1078 "Unknown menu tag: `$$Token'");
1086 // Read the languages file `name'
1087 bool LyX::readLanguagesFile(string const & name)
1089 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1091 string const lang_path = libFileSearch(string(), name);
1092 if (lang_path.empty()) {
1093 showFileError(name);
1096 languages.read(lang_path);
1101 // Read the encodings file `name'
1102 bool LyX::readEncodingsFile(string const & name)
1104 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1106 string const enc_path = libFileSearch(string(), name);
1107 if (enc_path.empty()) {
1108 showFileError(name);
1111 encodings.read(enc_path);
1120 /// return the the number of arguments consumed
1121 typedef boost::function<int(string const &, string const &)> cmd_helper;
1123 int parse_dbg(string const & arg, string const &)
1126 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1127 Debug::showTags(lyxerr);
1130 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1132 lyxerr.level(Debug::value(arg));
1133 Debug::showLevel(lyxerr, lyxerr.level());
1138 int parse_help(string const &, string const &)
1141 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1142 "Command line switches (case sensitive):\n"
1143 "\t-help summarize LyX usage\n"
1144 "\t-userdir dir set user directory to dir\n"
1145 "\t-sysdir dir set system directory to dir\n"
1146 "\t-geometry WxH+X+Y set geometry of the main window\n"
1147 "\t-dbg feature[,feature]...\n"
1148 " select the features to debug.\n"
1149 " Type `lyx -dbg' to see the list of features\n"
1150 "\t-x [--execute] command\n"
1151 " where command is a lyx command.\n"
1152 "\t-e [--export] fmt\n"
1153 " where fmt is the export format of choice.\n"
1154 "\t-i [--import] fmt file.xxx\n"
1155 " where fmt is the import format of choice\n"
1156 " and file.xxx is the file to be imported.\n"
1157 "\t-version summarize version and build info\n"
1158 "Check the LyX man page for more details.")) << endl;
1163 int parse_version(string const &, string const &)
1165 lyxerr << "LyX " << lyx_version
1166 << " (" << lyx_release_date << ")" << endl;
1167 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1169 lyxerr << lyx_version_info << endl;
1174 int parse_sysdir(string const & arg, string const &)
1177 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1180 cl_system_support = arg;
1184 int parse_userdir(string const & arg, string const &)
1187 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1190 cl_user_support = arg;
1194 int parse_execute(string const & arg, string const &)
1197 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1204 int parse_export(string const & type, string const &)
1207 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1208 "--export switch")) << endl;
1211 batch = "buffer-export " + type;
1216 int parse_import(string const & type, string const & file)
1219 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1220 "--import switch")) << endl;
1224 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1228 batch = "buffer-import " + type + ' ' + file;
1235 void LyX::easyParse(int & argc, char * argv[])
1237 std::map<string, cmd_helper> cmdmap;
1239 cmdmap["-dbg"] = parse_dbg;
1240 cmdmap["-help"] = parse_help;
1241 cmdmap["--help"] = parse_help;
1242 cmdmap["-version"] = parse_version;
1243 cmdmap["--version"] = parse_version;
1244 cmdmap["-sysdir"] = parse_sysdir;
1245 cmdmap["-userdir"] = parse_userdir;
1246 cmdmap["-x"] = parse_execute;
1247 cmdmap["--execute"] = parse_execute;
1248 cmdmap["-e"] = parse_export;
1249 cmdmap["--export"] = parse_export;
1250 cmdmap["-i"] = parse_import;
1251 cmdmap["--import"] = parse_import;
1253 for (int i = 1; i < argc; ++i) {
1254 std::map<string, cmd_helper>::const_iterator it
1255 = cmdmap.find(argv[i]);
1257 // check for X11 -geometry option
1258 if (support::compare(argv[i], "-geometry") == 0)
1259 geometryOption_ = true;
1261 // don't complain if not found - may be parsed later
1262 if (it == cmdmap.end())
1265 string arg((i + 1 < argc) ? argv[i + 1] : "");
1266 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1268 int const remove = 1 + it->second(arg, arg2);
1270 // Now, remove used arguments by shifting
1271 // the following ones remove places down.
1273 for (int j = i; j < argc; ++j)
1274 argv[j] = argv[j + remove];
1278 batch_command = batch;
1282 FuncStatus getStatus(FuncRequest const & action)
1284 return LyX::ref().lyxFunc().getStatus(action);
1288 void dispatch(FuncRequest const & action)
1290 LyX::ref().lyxFunc().dispatch(action);
1294 BufferList & theBufferList()
1296 return LyX::ref().bufferList();
1300 LyXFunc & theLyXFunc()
1302 return LyX::ref().lyxFunc();
1306 LyXServer & theLyXServer()
1308 // FIXME: this should not be use_gui dependent
1309 BOOST_ASSERT(use_gui);
1310 return LyX::ref().server();
1314 LyXServerSocket & theLyXServerSocket()
1316 // FIXME: this should not be use_gui dependent
1317 BOOST_ASSERT(use_gui);
1318 return LyX::ref().socket();
1322 kb_keymap & theTopLevelKeymap()
1324 BOOST_ASSERT(use_gui);
1325 return LyX::ref().topLevelKeymap();