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;
333 // FIXME: create a ConsoleApplication
334 return execBatchCommands(argc, argv, files);
337 // Force adding of font path _before_ Application is initialized
338 support::addFontResources();
340 // Let the frontend parse and remove all arguments that it knows
341 pimpl_->application_.reset(createApplication(argc, argv));
343 // Parse and remove all known arguments in the LyX singleton
344 // Give an error for all remaining ones.
345 int exit_status = execBatchCommands(argc, argv, files);
347 // Kill the application object before exiting.
348 pimpl_->application_.reset();
355 // FIXME: this global pointer should probably go.
356 theApp = pimpl_->application_.get();
357 restoreGuiSession(files);
358 // Start the real execution loop.
361 /* Create a CoreApplication class that will provide the main event loop
362 * and the socket callback registering. With Qt4, only QtCore
363 * library would be needed.
364 * When this is done, a server_mode could be created and the following two
365 * line would be moved out from here.
367 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
368 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
369 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
371 // handle the batch commands the user asked for
372 if (!batch_command.empty()) {
373 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
376 exit_status = pimpl_->application_->start(batch_command);
377 // Kill the application object before exiting. This avoid crash
379 pimpl_->application_.reset();
380 // Restore original font resources after Application is destroyed.
381 support::restoreFontResources();
387 void LyX::prepareExit()
389 // Set a flag that we do quitting from the program,
390 // so no refreshes are necessary.
393 // close buffers first
394 pimpl_->buffer_list_.closeAll();
396 // do any other cleanup procedures now
397 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
399 if (!destroyDir(package().temp_dir())) {
400 docstring const msg =
401 bformat(_("Unable to remove the temporary directory %1$s"),
402 from_utf8(package().temp_dir()));
403 Alert::warning(_("Unable to remove temporary directory"), msg);
408 void LyX::earlyExit(int status)
410 BOOST_ASSERT(pimpl_->application_.get());
411 // LyX::pimpl_::application_ is not initialised at this
412 // point so it's safe to just exit after some cleanup.
420 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
425 pimpl_->session_->writeFile();
426 pimpl_->lyx_server_.reset();
427 pimpl_->lyx_socket_.reset();
428 pimpl_->application_->exit(0);
434 int LyX::execBatchCommands(int & argc, char * argv[],
435 vector<string> & files)
437 // check for any spurious extra arguments
438 // other than documents
439 for (int argi = 1; argi < argc ; ++argi) {
440 if (argv[argi][0] == '-') {
442 bformat(_("Wrong command line option `%1$s'. Exiting."),
443 from_utf8(argv[argi]))) << endl;
448 // Initialization of LyX (reads lyxrc and more)
449 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
450 bool success = init();
451 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
455 for (int argi = argc - 1; argi >= 1; --argi) {
456 // check for any remaining extra arguments other than
457 // document file names. These will be passed out to the
459 if (argv[argi][0] == '-')
461 files.push_back(os::internal_path(argv[argi]));
465 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
467 // Execute batch commands if available
468 if (!batch_command.empty()) {
470 lyxerr[Debug::INIT] << "About to handle -x '"
471 << batch_command << '\'' << endl;
473 Buffer * last_loaded = 0;
475 vector<string>::const_iterator it = files.begin();
476 vector<string>::const_iterator end = files.end();
478 for (; it != end; ++it) {
479 // get absolute path of file and add ".lyx" to
480 // the filename if necessary
481 string s = fileSearch(string(), *it, "lyx");
483 Buffer * const b = newFile(*it, string(), true);
487 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
488 if (loadLyXFile(buf, s)) {
490 ErrorList const & el = buf->errorList("Parse");
492 for_each(el.begin(), el.end(),
493 boost::bind(&LyX::printError, this, _1));
496 pimpl_->buffer_list_.release(buf);
500 // try to dispatch to last loaded buffer first
503 if (last_loaded->dispatch(batch_command, &success)) {
508 files.clear(); // the files are already loaded
515 void LyX::restoreGuiSession(vector<string> const & files)
517 LyXView * view = newLyXView();
520 for_each(files.begin(), files.end(),
521 bind(&LyXView::loadLyXFile, view, _1, true));
523 // if a file is specified, I assume that user wants to edit *that* file
524 if (files.empty() && lyxrc.load_session) {
525 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
526 // do not add to the lastfile list since these files are restored from
527 // last seesion, and should be already there (regular files), or should
528 // not be added at all (help files).
529 for_each(lastopened.begin(), lastopened.end(),
530 bind(&LyXView::loadLyXFile, view, _1, false));
532 // clear this list to save a few bytes of RAM
533 pimpl_->session_->lastOpened().clear();
537 LyXView * LyX::newLyXView()
542 // determine windows size and position, from lyxrc and/or session
544 unsigned int width = 690;
545 unsigned int height = 510;
546 bool maximize = false;
548 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
549 width = lyxrc.geometry_width;
550 height = lyxrc.geometry_height;
552 // if lyxrc returns (0,0), then use session info
554 string val = session().sessionInfo().load("WindowWidth");
556 width = convert<unsigned int>(val);
557 val = session().sessionInfo().load("WindowHeight");
559 height = convert<unsigned int>(val);
560 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
564 // if user wants to restore window position
567 if (lyxrc.geometry_xysaved) {
568 string val = session().sessionInfo().load("WindowPosX");
570 posx = convert<int>(val);
571 val = session().sessionInfo().load("WindowPosY");
573 posy = convert<int>(val);
576 if (geometryOption_) {
580 // create the main window
581 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
589 The SIGHUP signal does not exist on Windows and does not need to be handled.
591 Windows handles SIGFPE and SIGSEGV signals as expected.
593 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
594 cause a new thread to be spawned. This may well result in unexpected
595 behaviour by the single-threaded LyX.
597 SIGTERM signals will come only from another process actually sending
598 that signal using 'raise' in Windows' POSIX compatability layer. It will
599 not come from the general "terminate process" methods that everyone
600 actually uses (and which can't be trapped). Killing an app 'politely' on
601 Windows involves first sending a WM_CLOSE message, something that is
602 caught already by the Qt frontend.
604 For more information see:
606 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
607 ...signals are mostly useless on Windows for a variety of reasons that are
610 'UNIX Application Migration Guide, Chapter 9'
611 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
613 'How To Terminate an Application "Cleanly" in Win32'
614 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
618 static void error_handler(int err_sig)
620 // Throw away any signals other than the first one received.
621 static sig_atomic_t handling_error = false;
624 handling_error = true;
626 // We have received a signal indicating a fatal error, so
627 // try and save the data ASAP.
628 LyX::cref().emergencyCleanup();
630 // These lyxerr calls may or may not work:
632 // Signals are asynchronous, so the main program may be in a very
633 // fragile state when a signal is processed and thus while a signal
634 // handler function executes.
635 // In general, therefore, we should avoid performing any
636 // I/O operations or calling most library and system functions from
639 // This shouldn't matter here, however, as we've already invoked
644 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
648 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
651 lyxerr << "\nlyx: SIGSEGV signal caught\n"
652 "Sorry, you have found a bug in LyX. "
653 "Please read the bug-reporting instructions "
654 "in Help->Introduction and send us a bug report, "
655 "if necessary. Thanks !\nBye." << endl;
663 // Deinstall the signal handlers
665 signal(SIGHUP, SIG_DFL);
667 signal(SIGINT, SIG_DFL);
668 signal(SIGFPE, SIG_DFL);
669 signal(SIGSEGV, SIG_DFL);
670 signal(SIGTERM, SIG_DFL);
673 if (err_sig == SIGSEGV ||
674 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
676 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
685 void LyX::printError(ErrorItem const & ei)
687 docstring tmp = _("LyX: ") + ei.error + char_type(':')
689 std::cerr << to_utf8(tmp) << std::endl;
693 void LyX::initGuiFont()
695 if (lyxrc.roman_font_name.empty())
696 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
698 if (lyxrc.sans_font_name.empty())
699 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
701 if (lyxrc.typewriter_font_name.empty())
702 lyxrc.typewriter_font_name
703 = pimpl_->application_->typewriterFontName();
710 signal(SIGHUP, error_handler);
712 signal(SIGFPE, error_handler);
713 signal(SIGSEGV, error_handler);
714 signal(SIGINT, error_handler);
715 signal(SIGTERM, error_handler);
716 // SIGPIPE can be safely ignored.
718 lyxrc.tempdir_path = package().temp_dir();
719 lyxrc.document_path = package().document_dir();
721 if (lyxrc.template_path.empty()) {
722 lyxrc.template_path = addPath(package().system_support(),
727 // Read configuration files
730 // This one may have been distributed along with LyX.
731 if (!readRcFile("lyxrc.dist"))
734 // Set the PATH correctly.
735 #if !defined (USE_POSIX_PACKAGING)
736 // Add the directory containing the LyX executable to the path
737 // so that LyX can find things like tex2lyx.
738 if (package().build_support().empty())
739 prependEnvPath("PATH", package().binary_dir());
741 if (!lyxrc.path_prefix.empty())
742 prependEnvPath("PATH", lyxrc.path_prefix);
744 // Check that user LyX directory is ok.
745 if (queryUserLyXDir(package().explicit_user_support()))
746 reconfigureUserLyXDir();
748 // no need for a splash when there is no GUI
753 // This one is generated in user_support directory by lib/configure.py.
754 if (!readRcFile("lyxrc.defaults"))
757 // Query the OS to know what formats are viewed natively
758 formats.setAutoOpen();
760 // Read lyxrc.dist again to be able to override viewer auto-detection.
761 readRcFile("lyxrc.dist");
763 system_lyxrc = lyxrc;
764 system_formats = formats;
765 system_converters = converters;
766 system_movers = movers;
767 system_lcolor = lcolor;
769 // This one is edited through the preferences dialog.
770 if (!readRcFile("preferences"))
773 if (!readEncodingsFile("encodings"))
775 if (!readLanguagesFile("languages"))
779 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
785 pimpl_->toplevel_keymap_.reset(new kb_keymap);
786 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
787 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
789 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
792 if (!readUIFile(lyxrc.ui_file))
796 if (lyxerr.debugging(Debug::LYXRC))
799 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
800 if (!lyxrc.path_prefix.empty())
801 prependEnvPath("PATH", lyxrc.path_prefix);
803 if (fs::exists(lyxrc.document_path) &&
804 fs::is_directory(lyxrc.document_path))
805 package().document_dir() = lyxrc.document_path;
807 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
808 if (package().temp_dir().empty()) {
809 Alert::error(_("Could not create temporary directory"),
810 bformat(_("Could not create a temporary directory in\n"
811 "%1$s. Make sure that this\n"
812 "path exists and is writable and try again."),
813 from_utf8(lyxrc.tempdir_path)));
814 // createLyXTmpDir() tries sufficiently hard to create a
815 // usable temp dir, so the probability to come here is
816 // close to zero. We therefore don't try to overcome this
817 // problem with e.g. asking the user for a new path and
818 // trying again but simply exit.
822 if (lyxerr.debugging(Debug::INIT)) {
823 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
826 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
827 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
829 // This must happen after package initialization and after lyxrc is
830 // read, therefore it can't be done by a static object.
831 ConverterCache::init();
837 void LyX::defaultKeyBindings(kb_keymap * kbmap)
839 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
840 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
841 kbmap->bind("Up", FuncRequest(LFUN_UP));
842 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
844 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
845 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
846 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
847 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
849 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
850 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
851 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
852 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
854 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
855 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
857 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
858 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
860 // kbmap->bindings to enable the use of the numeric keypad
862 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
863 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
864 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
865 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
866 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
867 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
868 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
869 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
870 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
871 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
872 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
873 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
874 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
875 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
876 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
877 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
878 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
879 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
880 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
881 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
882 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
883 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
884 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
885 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
889 void LyX::emergencyCleanup() const
891 // what to do about tmpfiles is non-obvious. we would
892 // like to delete any we find, but our lyxdir might
893 // contain documents etc. which might be helpful on
896 pimpl_->buffer_list_.emergencyWriteAll();
898 pimpl_->lyx_server_->emergencyCleanup();
899 pimpl_->lyx_server_.reset();
900 pimpl_->lyx_socket_.reset();
905 void LyX::deadKeyBindings(kb_keymap * kbmap)
907 // bindKeyings for transparent handling of deadkeys
908 // The keysyms are gotten from XFree86 X11R6
909 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
910 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
911 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
912 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
913 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
914 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
915 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
916 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
917 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
918 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
919 // nothing with this name
920 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
921 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
922 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
923 // nothing with this name either...
924 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
925 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
926 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
927 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
933 // return true if file does not exist or is older than configure.py.
934 bool needsUpdate(string const & file)
936 static string const configure_script =
937 addName(package().system_support(), "configure.py");
938 string const absfile =
939 addName(package().user_support(), file);
941 return (! fs::exists(absfile))
942 || (fs::last_write_time(configure_script)
943 > fs::last_write_time(absfile));
949 bool LyX::queryUserLyXDir(bool explicit_userdir)
951 // Does user directory exist?
952 if (fs::exists(package().user_support()) &&
953 fs::is_directory(package().user_support())) {
956 return needsUpdate("lyxrc.defaults")
957 || needsUpdate("textclass.lst")
958 || needsUpdate("packages.lst");
961 first_start = !explicit_userdir;
963 // If the user specified explicitly a directory, ask whether
964 // to create it. If the user says "no", then exit.
965 if (explicit_userdir &&
967 _("Missing user LyX directory"),
968 bformat(_("You have specified a non-existent user "
969 "LyX directory, %1$s.\n"
970 "It is needed to keep your own configuration."),
971 from_utf8(package().user_support())),
973 _("&Create directory"),
975 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
976 earlyExit(EXIT_FAILURE);
979 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
980 from_utf8(package().user_support())))
983 if (!createDirectory(package().user_support(), 0755)) {
984 // Failed, so let's exit.
985 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
987 earlyExit(EXIT_FAILURE);
994 bool LyX::readRcFile(string const & name)
996 lyxerr[Debug::INIT] << "About to read " << name << "... ";
998 string const lyxrc_path = libFileSearch(string(), name);
999 if (!lyxrc_path.empty()) {
1001 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1003 if (lyxrc.read(lyxrc_path) < 0) {
1004 showFileError(name);
1008 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1014 // Read the ui file `name'
1015 bool LyX::readUIFile(string const & name)
1025 struct keyword_item uitags[ui_last - 1] = {
1026 { "include", ui_include },
1027 { "menuset", ui_menuset },
1028 { "toolbar", ui_toolbar },
1029 { "toolbars", ui_toolbars }
1032 // Ensure that a file is read only once (prevents include loops)
1033 static std::list<string> uifiles;
1034 std::list<string>::const_iterator it = uifiles.begin();
1035 std::list<string>::const_iterator end = uifiles.end();
1036 it = std::find(it, end, name);
1038 lyxerr[Debug::INIT] << "UI file '" << name
1039 << "' has been read already. "
1040 << "Is this an include loop?"
1045 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1047 string const ui_path = libFileSearch("ui", name, "ui");
1049 if (ui_path.empty()) {
1050 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1051 showFileError(name);
1054 uifiles.push_back(name);
1056 lyxerr[Debug::INIT] << "Found " << name
1057 << " in " << ui_path << endl;
1058 LyXLex lex(uitags, ui_last - 1);
1059 lex.setFile(ui_path);
1061 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1065 if (lyxerr.debugging(Debug::PARSER))
1066 lex.printTable(lyxerr);
1068 while (lex.isOK()) {
1069 switch (lex.lex()) {
1072 string const file = lex.getString();
1073 if (!readUIFile(file))
1078 menubackend.read(lex);
1082 toolbarbackend.read(lex);
1086 toolbarbackend.readToolbars(lex);
1090 if (!rtrim(lex.getString()).empty())
1091 lex.printError("LyX::ReadUIFile: "
1092 "Unknown menu tag: `$$Token'");
1100 // Read the languages file `name'
1101 bool LyX::readLanguagesFile(string const & name)
1103 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1105 string const lang_path = libFileSearch(string(), name);
1106 if (lang_path.empty()) {
1107 showFileError(name);
1110 languages.read(lang_path);
1115 // Read the encodings file `name'
1116 bool LyX::readEncodingsFile(string const & name)
1118 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1120 string const enc_path = libFileSearch(string(), name);
1121 if (enc_path.empty()) {
1122 showFileError(name);
1125 encodings.read(enc_path);
1134 /// return the the number of arguments consumed
1135 typedef boost::function<int(string const &, string const &)> cmd_helper;
1137 int parse_dbg(string const & arg, string const &)
1140 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1141 Debug::showTags(lyxerr);
1144 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1146 lyxerr.level(Debug::value(arg));
1147 Debug::showLevel(lyxerr, lyxerr.level());
1152 int parse_help(string const &, string const &)
1155 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1156 "Command line switches (case sensitive):\n"
1157 "\t-help summarize LyX usage\n"
1158 "\t-userdir dir set user directory to dir\n"
1159 "\t-sysdir dir set system directory to dir\n"
1160 "\t-geometry WxH+X+Y set geometry of the main window\n"
1161 "\t-dbg feature[,feature]...\n"
1162 " select the features to debug.\n"
1163 " Type `lyx -dbg' to see the list of features\n"
1164 "\t-x [--execute] command\n"
1165 " where command is a lyx command.\n"
1166 "\t-e [--export] fmt\n"
1167 " where fmt is the export format of choice.\n"
1168 "\t-i [--import] fmt file.xxx\n"
1169 " where fmt is the import format of choice\n"
1170 " and file.xxx is the file to be imported.\n"
1171 "\t-version summarize version and build info\n"
1172 "Check the LyX man page for more details.")) << endl;
1177 int parse_version(string const &, string const &)
1179 lyxerr << "LyX " << lyx_version
1180 << " (" << lyx_release_date << ")" << endl;
1181 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1183 lyxerr << lyx_version_info << endl;
1188 int parse_sysdir(string const & arg, string const &)
1191 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1194 cl_system_support = arg;
1198 int parse_userdir(string const & arg, string const &)
1201 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1204 cl_user_support = arg;
1208 int parse_execute(string const & arg, string const &)
1211 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1218 int parse_export(string const & type, string const &)
1221 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1222 "--export switch")) << endl;
1225 batch = "buffer-export " + type;
1230 int parse_import(string const & type, string const & file)
1233 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1234 "--import switch")) << endl;
1238 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1242 batch = "buffer-import " + type + ' ' + file;
1249 void LyX::easyParse(int & argc, char * argv[])
1251 std::map<string, cmd_helper> cmdmap;
1253 cmdmap["-dbg"] = parse_dbg;
1254 cmdmap["-help"] = parse_help;
1255 cmdmap["--help"] = parse_help;
1256 cmdmap["-version"] = parse_version;
1257 cmdmap["--version"] = parse_version;
1258 cmdmap["-sysdir"] = parse_sysdir;
1259 cmdmap["-userdir"] = parse_userdir;
1260 cmdmap["-x"] = parse_execute;
1261 cmdmap["--execute"] = parse_execute;
1262 cmdmap["-e"] = parse_export;
1263 cmdmap["--export"] = parse_export;
1264 cmdmap["-i"] = parse_import;
1265 cmdmap["--import"] = parse_import;
1267 for (int i = 1; i < argc; ++i) {
1268 std::map<string, cmd_helper>::const_iterator it
1269 = cmdmap.find(argv[i]);
1271 // check for X11 -geometry option
1272 if (support::compare(argv[i], "-geometry") == 0)
1273 geometryOption_ = true;
1275 // don't complain if not found - may be parsed later
1276 if (it == cmdmap.end())
1279 string arg((i + 1 < argc) ? argv[i + 1] : "");
1280 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1282 int const remove = 1 + it->second(arg, arg2);
1284 // Now, remove used arguments by shifting
1285 // the following ones remove places down.
1287 for (int j = i; j < argc; ++j)
1288 argv[j] = argv[j + remove];
1292 batch_command = batch;
1296 FuncStatus getStatus(FuncRequest const & action)
1298 return LyX::ref().lyxFunc().getStatus(action);
1302 void dispatch(FuncRequest const & action)
1304 LyX::ref().lyxFunc().dispatch(action);
1308 BufferList & theBufferList()
1310 return LyX::ref().bufferList();
1314 LyXFunc & theLyXFunc()
1316 return LyX::ref().lyxFunc();
1320 LyXServer & theLyXServer()
1322 // FIXME: this should not be use_gui dependent
1323 BOOST_ASSERT(use_gui);
1324 return LyX::ref().server();
1328 LyXServerSocket & theLyXServerSocket()
1330 // FIXME: this should not be use_gui dependent
1331 BOOST_ASSERT(use_gui);
1332 return LyX::ref().socket();
1336 kb_keymap & theTopLevelKeymap()
1338 BOOST_ASSERT(use_gui);
1339 return LyX::ref().topLevelKeymap();
1343 IconvProcessor & utf8ToUcs4()
1345 return LyX::ref().iconvProcessor();