3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
21 #include "buffer_funcs.h"
22 #include "bufferlist.h"
23 #include "converter.h"
26 #include "errorlist.h"
34 #include "LyXAction.h"
38 #include "lyxserver.h"
39 #include "lyxsocket.h"
40 #include "lyxtextclasslist.h"
41 #include "MenuBackend.h"
43 #include "ToolbarBackend.h"
45 #include "frontends/Alert.h"
46 #include "frontends/Application.h"
47 #include "frontends/Gui.h"
48 #include "frontends/LyXView.h"
50 #include "support/environment.h"
51 #include "support/filetools.h"
52 #include "support/fontutils.h"
53 #include "support/lyxlib.h"
54 #include "support/convert.h"
55 #include "support/os.h"
56 #include "support/package.h"
57 #include "support/path.h"
58 #include "support/systemcall.h"
60 #include <boost/bind.hpp>
61 #include <boost/filesystem/operations.hpp>
69 using support::addName;
70 using support::addPath;
71 using support::bformat;
72 using support::createDirectory;
73 using support::createLyXTmpDir;
74 using support::destroyDir;
75 using support::fileSearch;
76 using support::getEnv;
77 using support::i18nLibFileSearch;
78 using support::libFileSearch;
79 using support::package;
80 using support::prependEnvPath;
82 using support::Systemcall;
84 namespace Alert = frontend::Alert;
85 namespace os = support::os;
86 namespace fs = boost::filesystem;
93 #ifndef CXX_GLOBAL_CSTD
100 frontend::Application * theApp = 0;
102 /// are we using the GUI at all?
104 * We default to true and this is changed to false when the export feature is used.
111 // Filled with the command line arguments "foo" of "-sysdir foo" or
113 string cl_system_support;
114 string cl_user_support;
117 void showFileError(string const & error)
119 Alert::warning(_("Could not read configuration file"),
120 bformat(_("Error while reading the configuration file\n%1$s.\n"
121 "Please check your installation."), from_utf8(error)));
125 void reconfigureUserLyXDir()
127 string const configure_command = package().configure_command();
129 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
130 support::Path p(package().user_support());
132 one.startscript(Systemcall::Wait, configure_command);
133 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
139 /// The main application class private implementation.
140 struct LyX::Singletons
142 /// our function handler
145 BufferList buffer_list_;
147 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
149 boost::scoped_ptr<LyXServer> lyx_server_;
151 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
153 boost::scoped_ptr<frontend::Application> application_;
154 /// lyx session, containing lastfiles, lastfilepos, and lastopened
155 boost::scoped_ptr<Session> session_;
159 boost::scoped_ptr<LyX> LyX::singleton_;
162 int LyX::exec(int & argc, char * argv[])
164 BOOST_ASSERT(!singleton_.get());
165 // We must return from this before launching the gui so that
166 // other parts of the code can access singleton_ through
167 // LyX::ref and LyX::cref.
168 singleton_.reset(new LyX);
169 // Start the real execution loop.
170 return singleton_->priv_exec(argc, argv);
176 BOOST_ASSERT(singleton_.get());
177 return *singleton_.get();
181 LyX const & LyX::cref()
183 BOOST_ASSERT(singleton_.get());
184 return *singleton_.get();
189 : first_start(false), geometryOption_(false)
191 pimpl_.reset(new Singletons);
195 BufferList & LyX::bufferList()
197 return pimpl_->buffer_list_;
201 BufferList const & LyX::bufferList() const
203 return pimpl_->buffer_list_;
207 Session & LyX::session()
209 BOOST_ASSERT(pimpl_->session_.get());
210 return *pimpl_->session_.get();
214 Session const & LyX::session() const
216 BOOST_ASSERT(pimpl_->session_.get());
217 return *pimpl_->session_.get();
221 LyXFunc & LyX::lyxFunc()
223 return pimpl_->lyxfunc_;
227 LyXFunc const & LyX::lyxFunc() const
229 return pimpl_->lyxfunc_;
233 LyXServer & LyX::server()
235 BOOST_ASSERT(pimpl_->lyx_server_.get());
236 return *pimpl_->lyx_server_.get();
240 LyXServer const & LyX::server() const
242 BOOST_ASSERT(pimpl_->lyx_server_.get());
243 return *pimpl_->lyx_server_.get();
247 LyXServerSocket & LyX::socket()
249 BOOST_ASSERT(pimpl_->lyx_socket_.get());
250 return *pimpl_->lyx_socket_.get();
254 LyXServerSocket const & LyX::socket() const
256 BOOST_ASSERT(pimpl_->lyx_socket_.get());
257 return *pimpl_->lyx_socket_.get();
261 frontend::Application & LyX::application()
263 BOOST_ASSERT(pimpl_->application_.get());
264 return *pimpl_->application_.get();
268 frontend::Application const & LyX::application() const
270 BOOST_ASSERT(pimpl_->application_.get());
271 return *pimpl_->application_.get();
275 kb_keymap & LyX::topLevelKeymap()
277 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
278 return *pimpl_->toplevel_keymap_.get();
282 kb_keymap const & LyX::topLevelKeymap() const
284 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
285 return *pimpl_->toplevel_keymap_.get();
289 Buffer const * const LyX::updateInset(InsetBase const * inset) const
294 Buffer const * buffer_ptr = 0;
295 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
296 vector<int>::const_iterator it = view_ids.begin();
297 vector<int>::const_iterator const end = view_ids.end();
298 for (; it != end; ++it) {
300 pimpl_->application_->gui().view(*it).updateInset(inset);
308 int LyX::priv_exec(int & argc, char * argv[])
310 // Here we need to parse the command line. At least
311 // we need to parse for "-dbg" and "-help"
312 easyParse(argc, argv);
314 support::init_package(argv[0], cl_system_support, cl_user_support,
315 support::top_build_dir_is_one_level_up);
317 vector<string> files;
318 int exit_status = execBatchCommands(argc, argv, files);
324 // Force adding of font path _before_ Application is initialized
325 support::addFontResources();
326 pimpl_->application_.reset(createApplication(argc, argv));
328 // FIXME: this global pointer should probably go.
329 theApp = pimpl_->application_.get();
330 restoreGuiSession(files);
331 // Start the real execution loop.
334 /* Create a CoreApplication class that will provide the main event loop
335 * and the socket callback registering. With Qt4, only QtCore
336 * library would be needed.
337 * When this is done, a server_mode could be created and the following two
338 * line would be moved out from here.
340 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
341 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
342 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
344 // handle the batch commands the user asked for
345 if (!batch_command.empty()) {
346 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
349 exit_status = pimpl_->application_->start(batch_command);
350 // Kill the application object before exiting. This avoid crash
352 pimpl_->application_.reset();
353 // Restore original font resources after Application is destroyed.
354 support::restoreFontResources();
357 // FIXME: create a ConsoleApplication
365 void LyX::prepareExit()
367 // Set a flag that we do quitting from the program,
368 // so no refreshes are necessary.
371 // close buffers first
372 pimpl_->buffer_list_.closeAll();
374 // do any other cleanup procedures now
375 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
377 if (!destroyDir(package().temp_dir())) {
378 docstring const msg =
379 bformat(_("Unable to remove the temporary directory %1$s"),
380 from_utf8(package().temp_dir()));
381 Alert::warning(_("Unable to remove temporary directory"), msg);
386 void LyX::earlyExit(int status)
388 BOOST_ASSERT(pimpl_->application_.get());
389 // LyX::pimpl_::application_ is not initialised at this
390 // point so it's safe to just exit after some cleanup.
398 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
403 pimpl_->session_->writeFile();
404 pimpl_->lyx_server_.reset();
405 pimpl_->lyx_socket_.reset();
406 pimpl_->application_->exit(0);
411 int LyX::execBatchCommands(int & argc, char * argv[],
412 vector<string> & files)
414 // check for any spurious extra arguments
415 // other than documents
416 for (int argi = 1; argi < argc ; ++argi) {
417 if (argv[argi][0] == '-') {
419 bformat(_("Wrong command line option `%1$s'. Exiting."),
420 from_utf8(argv[argi]))) << endl;
425 // Initialization of LyX (reads lyxrc and more)
426 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
427 bool success = init();
428 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
432 for (int argi = argc - 1; argi >= 1; --argi)
433 files.push_back(os::internal_path(argv[argi]));
436 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
438 // Execute batch commands if available
439 if (!batch_command.empty()) {
441 lyxerr[Debug::INIT] << "About to handle -x '"
442 << batch_command << '\'' << endl;
444 Buffer * last_loaded = 0;
446 vector<string>::const_iterator it = files.begin();
447 vector<string>::const_iterator end = files.end();
449 for (; it != end; ++it) {
450 // get absolute path of file and add ".lyx" to
451 // the filename if necessary
452 string s = fileSearch(string(), *it, "lyx");
454 Buffer * const b = newFile(*it, string(), true);
458 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
459 if (loadLyXFile(buf, s)) {
461 ErrorList const & el = buf->errorList("Parse");
463 for_each(el.begin(), el.end(),
464 boost::bind(&LyX::printError, this, _1));
467 pimpl_->buffer_list_.release(buf);
471 // try to dispatch to last loaded buffer first
474 if (last_loaded->dispatch(batch_command, &success)) {
479 files.clear(); // the files are already loaded
486 void LyX::restoreGuiSession(vector<string> const & files)
488 LyXView * view = newLyXView();
491 for_each(files.begin(), files.end(),
492 bind(&LyXView::loadLyXFile, view, _1, true));
494 // if a file is specified, I assume that user wants to edit *that* file
495 if (files.empty() && lyxrc.load_session) {
496 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
497 // do not add to the lastfile list since these files are restored from
498 // last seesion, and should be already there (regular files), or should
499 // not be added at all (help files).
500 for_each(lastopened.begin(), lastopened.end(),
501 bind(&LyXView::loadLyXFile, view, _1, false));
503 // clear this list to save a few bytes of RAM
504 pimpl_->session_->lastOpened().clear();
508 LyXView * LyX::newLyXView()
513 // determine windows size and position, from lyxrc and/or session
515 unsigned int width = 690;
516 unsigned int height = 510;
517 bool maximize = false;
519 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
520 width = lyxrc.geometry_width;
521 height = lyxrc.geometry_height;
523 // if lyxrc returns (0,0), then use session info
525 string val = session().sessionInfo().load("WindowWidth");
527 width = convert<unsigned int>(val);
528 val = session().sessionInfo().load("WindowHeight");
530 height = convert<unsigned int>(val);
531 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
535 // if user wants to restore window position
538 if (lyxrc.geometry_xysaved) {
539 string val = session().sessionInfo().load("WindowPosX");
541 posx = convert<int>(val);
542 val = session().sessionInfo().load("WindowPosY");
544 posy = convert<int>(val);
547 if (geometryOption_) {
551 // create the main window
552 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
560 The SIGHUP signal does not exist on Windows and does not need to be handled.
562 Windows handles SIGFPE and SIGSEGV signals as expected.
564 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
565 cause a new thread to be spawned. This may well result in unexpected
566 behaviour by the single-threaded LyX.
568 SIGTERM signals will come only from another process actually sending
569 that signal using 'raise' in Windows' POSIX compatability layer. It will
570 not come from the general "terminate process" methods that everyone
571 actually uses (and which can't be trapped). Killing an app 'politely' on
572 Windows involves first sending a WM_CLOSE message, something that is
573 caught already by the Qt frontend.
575 For more information see:
577 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
578 ...signals are mostly useless on Windows for a variety of reasons that are
581 'UNIX Application Migration Guide, Chapter 9'
582 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
584 'How To Terminate an Application "Cleanly" in Win32'
585 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
589 static void error_handler(int err_sig)
591 // Throw away any signals other than the first one received.
592 static sig_atomic_t handling_error = false;
595 handling_error = true;
597 // We have received a signal indicating a fatal error, so
598 // try and save the data ASAP.
599 LyX::cref().emergencyCleanup();
601 // These lyxerr calls may or may not work:
603 // Signals are asynchronous, so the main program may be in a very
604 // fragile state when a signal is processed and thus while a signal
605 // handler function executes.
606 // In general, therefore, we should avoid performing any
607 // I/O operations or calling most library and system functions from
610 // This shouldn't matter here, however, as we've already invoked
615 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
619 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
622 lyxerr << "\nlyx: SIGSEGV signal caught\n"
623 "Sorry, you have found a bug in LyX. "
624 "Please read the bug-reporting instructions "
625 "in Help->Introduction and send us a bug report, "
626 "if necessary. Thanks !\nBye." << endl;
634 // Deinstall the signal handlers
636 signal(SIGHUP, SIG_DFL);
638 signal(SIGINT, SIG_DFL);
639 signal(SIGFPE, SIG_DFL);
640 signal(SIGSEGV, SIG_DFL);
641 signal(SIGTERM, SIG_DFL);
644 if (err_sig == SIGSEGV ||
645 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
647 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
656 void LyX::printError(ErrorItem const & ei)
658 docstring tmp = _("LyX: ") + ei.error + char_type(':')
660 std::cerr << to_utf8(tmp) << std::endl;
664 void LyX::initGuiFont()
666 if (lyxrc.roman_font_name.empty())
667 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
669 if (lyxrc.sans_font_name.empty())
670 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
672 if (lyxrc.typewriter_font_name.empty())
673 lyxrc.typewriter_font_name
674 = pimpl_->application_->typewriterFontName();
681 signal(SIGHUP, error_handler);
683 signal(SIGFPE, error_handler);
684 signal(SIGSEGV, error_handler);
685 signal(SIGINT, error_handler);
686 signal(SIGTERM, error_handler);
687 // SIGPIPE can be safely ignored.
689 lyxrc.tempdir_path = package().temp_dir();
690 lyxrc.document_path = package().document_dir();
692 if (lyxrc.template_path.empty()) {
693 lyxrc.template_path = addPath(package().system_support(),
698 // Read configuration files
701 // This one may have been distributed along with LyX.
702 if (!readRcFile("lyxrc.dist"))
705 // Set the PATH correctly.
706 #if !defined (USE_POSIX_PACKAGING)
707 // Add the directory containing the LyX executable to the path
708 // so that LyX can find things like tex2lyx.
709 if (package().build_support().empty())
710 prependEnvPath("PATH", package().binary_dir());
712 if (!lyxrc.path_prefix.empty())
713 prependEnvPath("PATH", lyxrc.path_prefix);
715 // Check that user LyX directory is ok.
716 if (queryUserLyXDir(package().explicit_user_support()))
717 reconfigureUserLyXDir();
719 // no need for a splash when there is no GUI
724 // This one is generated in user_support directory by lib/configure.py.
725 if (!readRcFile("lyxrc.defaults"))
728 // Query the OS to know what formats are viewed natively
729 formats.setAutoOpen();
731 // Read lyxrc.dist again to be able to override viewer auto-detection.
732 readRcFile("lyxrc.dist");
734 system_lyxrc = lyxrc;
735 system_formats = formats;
736 system_converters = converters;
737 system_movers = movers;
738 system_lcolor = lcolor;
740 // This one is edited through the preferences dialog.
741 if (!readRcFile("preferences"))
744 if (!readEncodingsFile("encodings"))
746 if (!readLanguagesFile("languages"))
750 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
756 pimpl_->toplevel_keymap_.reset(new kb_keymap);
757 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
758 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
760 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
763 if (!readUIFile(lyxrc.ui_file))
767 if (lyxerr.debugging(Debug::LYXRC))
770 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
771 if (!lyxrc.path_prefix.empty())
772 prependEnvPath("PATH", lyxrc.path_prefix);
774 if (fs::exists(lyxrc.document_path) &&
775 fs::is_directory(lyxrc.document_path))
776 package().document_dir() = lyxrc.document_path;
778 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
779 if (package().temp_dir().empty()) {
780 Alert::error(_("Could not create temporary directory"),
781 bformat(_("Could not create a temporary directory in\n"
782 "%1$s. Make sure that this\n"
783 "path exists and is writable and try again."),
784 from_utf8(lyxrc.tempdir_path)));
785 // createLyXTmpDir() tries sufficiently hard to create a
786 // usable temp dir, so the probability to come here is
787 // close to zero. We therefore don't try to overcome this
788 // problem with e.g. asking the user for a new path and
789 // trying again but simply exit.
793 if (lyxerr.debugging(Debug::INIT)) {
794 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
797 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
798 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
803 void LyX::defaultKeyBindings(kb_keymap * kbmap)
805 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
806 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
807 kbmap->bind("Up", FuncRequest(LFUN_UP));
808 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
810 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
811 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
812 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
813 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
815 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
816 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
817 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
818 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
820 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
821 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
823 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
824 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
826 // kbmap->bindings to enable the use of the numeric keypad
828 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
829 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
830 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
831 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
832 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
833 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
834 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
835 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
836 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
837 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
838 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
839 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
840 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
841 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
842 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
843 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
844 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
845 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
846 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
847 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
848 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
849 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
850 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
851 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
855 void LyX::emergencyCleanup() const
857 // what to do about tmpfiles is non-obvious. we would
858 // like to delete any we find, but our lyxdir might
859 // contain documents etc. which might be helpful on
862 pimpl_->buffer_list_.emergencyWriteAll();
864 pimpl_->lyx_server_->emergencyCleanup();
865 pimpl_->lyx_server_.reset();
866 pimpl_->lyx_socket_.reset();
871 void LyX::deadKeyBindings(kb_keymap * kbmap)
873 // bindKeyings for transparent handling of deadkeys
874 // The keysyms are gotten from XFree86 X11R6
875 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
876 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
877 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
878 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
879 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
880 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
881 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
882 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
883 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
884 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
885 // nothing with this name
886 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
887 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
888 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
889 // nothing with this name either...
890 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
891 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
892 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
893 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
899 // return true if file does not exist or is older than configure.py.
900 bool needsUpdate(string const & file)
902 static string const configure_script =
903 addName(package().system_support(), "configure.py");
904 string const absfile =
905 addName(package().user_support(), file);
907 return (! fs::exists(absfile))
908 || (fs::last_write_time(configure_script)
909 > fs::last_write_time(absfile));
915 bool LyX::queryUserLyXDir(bool explicit_userdir)
917 // Does user directory exist?
918 if (fs::exists(package().user_support()) &&
919 fs::is_directory(package().user_support())) {
922 return needsUpdate("lyxrc.defaults")
923 || needsUpdate("textclass.lst")
924 || needsUpdate("packages.lst");
927 first_start = !explicit_userdir;
929 // If the user specified explicitly a directory, ask whether
930 // to create it. If the user says "no", then exit.
931 if (explicit_userdir &&
933 _("Missing user LyX directory"),
934 bformat(_("You have specified a non-existent user "
935 "LyX directory, %1$s.\n"
936 "It is needed to keep your own configuration."),
937 from_utf8(package().user_support())),
939 _("&Create directory"),
941 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
942 earlyExit(EXIT_FAILURE);
945 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
946 from_utf8(package().user_support())))
949 if (!createDirectory(package().user_support(), 0755)) {
950 // Failed, so let's exit.
951 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
953 earlyExit(EXIT_FAILURE);
960 bool LyX::readRcFile(string const & name)
962 lyxerr[Debug::INIT] << "About to read " << name << "... ";
964 string const lyxrc_path = libFileSearch(string(), name);
965 if (!lyxrc_path.empty()) {
967 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
969 if (lyxrc.read(lyxrc_path) < 0) {
974 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
980 // Read the ui file `name'
981 bool LyX::readUIFile(string const & name)
991 struct keyword_item uitags[ui_last - 1] = {
992 { "include", ui_include },
993 { "menuset", ui_menuset },
994 { "toolbar", ui_toolbar },
995 { "toolbars", ui_toolbars }
998 // Ensure that a file is read only once (prevents include loops)
999 static std::list<string> uifiles;
1000 std::list<string>::const_iterator it = uifiles.begin();
1001 std::list<string>::const_iterator end = uifiles.end();
1002 it = std::find(it, end, name);
1004 lyxerr[Debug::INIT] << "UI file '" << name
1005 << "' has been read already. "
1006 << "Is this an include loop?"
1011 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1013 string const ui_path = libFileSearch("ui", name, "ui");
1015 if (ui_path.empty()) {
1016 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1017 showFileError(name);
1020 uifiles.push_back(name);
1022 lyxerr[Debug::INIT] << "Found " << name
1023 << " in " << ui_path << endl;
1024 LyXLex lex(uitags, ui_last - 1);
1025 lex.setFile(ui_path);
1027 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1031 if (lyxerr.debugging(Debug::PARSER))
1032 lex.printTable(lyxerr);
1034 while (lex.isOK()) {
1035 switch (lex.lex()) {
1038 string const file = lex.getString();
1039 if (!readUIFile(file))
1044 menubackend.read(lex);
1048 toolbarbackend.read(lex);
1052 toolbarbackend.readToolbars(lex);
1056 if (!rtrim(lex.getString()).empty())
1057 lex.printError("LyX::ReadUIFile: "
1058 "Unknown menu tag: `$$Token'");
1066 // Read the languages file `name'
1067 bool LyX::readLanguagesFile(string const & name)
1069 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1071 string const lang_path = libFileSearch(string(), name);
1072 if (lang_path.empty()) {
1073 showFileError(name);
1076 languages.read(lang_path);
1081 // Read the encodings file `name'
1082 bool LyX::readEncodingsFile(string const & name)
1084 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1086 string const enc_path = libFileSearch(string(), name);
1087 if (enc_path.empty()) {
1088 showFileError(name);
1091 encodings.read(enc_path);
1100 /// return the the number of arguments consumed
1101 typedef boost::function<int(string const &, string const &)> cmd_helper;
1103 int parse_dbg(string const & arg, string const &)
1106 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1107 Debug::showTags(lyxerr);
1110 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1112 lyxerr.level(Debug::value(arg));
1113 Debug::showLevel(lyxerr, lyxerr.level());
1118 int parse_help(string const &, string const &)
1121 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1122 "Command line switches (case sensitive):\n"
1123 "\t-help summarize LyX usage\n"
1124 "\t-userdir dir set user directory to dir\n"
1125 "\t-sysdir dir set system directory to dir\n"
1126 "\t-geometry WxH+X+Y set geometry of the main window\n"
1127 "\t-dbg feature[,feature]...\n"
1128 " select the features to debug.\n"
1129 " Type `lyx -dbg' to see the list of features\n"
1130 "\t-x [--execute] command\n"
1131 " where command is a lyx command.\n"
1132 "\t-e [--export] fmt\n"
1133 " where fmt is the export format of choice.\n"
1134 "\t-i [--import] fmt file.xxx\n"
1135 " where fmt is the import format of choice\n"
1136 " and file.xxx is the file to be imported.\n"
1137 "\t-version summarize version and build info\n"
1138 "Check the LyX man page for more details.")) << endl;
1143 int parse_version(string const &, string const &)
1145 lyxerr << "LyX " << lyx_version
1146 << " (" << lyx_release_date << ")" << endl;
1147 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1149 lyxerr << lyx_version_info << endl;
1154 int parse_sysdir(string const & arg, string const &)
1157 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1160 cl_system_support = arg;
1164 int parse_userdir(string const & arg, string const &)
1167 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1170 cl_user_support = arg;
1174 int parse_execute(string const & arg, string const &)
1177 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1184 int parse_export(string const & type, string const &)
1187 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1188 "--export switch")) << endl;
1191 batch = "buffer-export " + type;
1196 int parse_import(string const & type, string const & file)
1199 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1200 "--import switch")) << endl;
1204 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1208 batch = "buffer-import " + type + ' ' + file;
1215 void LyX::easyParse(int & argc, char * argv[])
1217 std::map<string, cmd_helper> cmdmap;
1219 cmdmap["-dbg"] = parse_dbg;
1220 cmdmap["-help"] = parse_help;
1221 cmdmap["--help"] = parse_help;
1222 cmdmap["-version"] = parse_version;
1223 cmdmap["--version"] = parse_version;
1224 cmdmap["-sysdir"] = parse_sysdir;
1225 cmdmap["-userdir"] = parse_userdir;
1226 cmdmap["-x"] = parse_execute;
1227 cmdmap["--execute"] = parse_execute;
1228 cmdmap["-e"] = parse_export;
1229 cmdmap["--export"] = parse_export;
1230 cmdmap["-i"] = parse_import;
1231 cmdmap["--import"] = parse_import;
1233 for (int i = 1; i < argc; ++i) {
1234 std::map<string, cmd_helper>::const_iterator it
1235 = cmdmap.find(argv[i]);
1237 // check for X11 -geometry option
1238 if (support::compare(argv[i], "-geometry") == 0)
1239 geometryOption_ = true;
1241 // don't complain if not found - may be parsed later
1242 if (it == cmdmap.end())
1245 string arg((i + 1 < argc) ? argv[i + 1] : "");
1246 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1248 int const remove = 1 + it->second(arg, arg2);
1250 // Now, remove used arguments by shifting
1251 // the following ones remove places down.
1253 for (int j = i; j < argc; ++j)
1254 argv[j] = argv[j + remove];
1258 batch_command = batch;
1262 FuncStatus getStatus(FuncRequest const & action)
1264 return LyX::ref().lyxFunc().getStatus(action);
1268 void dispatch(FuncRequest const & action)
1270 LyX::ref().lyxFunc().dispatch(action);
1274 BufferList & theBufferList()
1276 return LyX::ref().bufferList();
1280 LyXFunc & theLyXFunc()
1282 return LyX::ref().lyxFunc();
1286 LyXServer & theLyXServer()
1288 // FIXME: this should not be use_gui dependent
1289 BOOST_ASSERT(use_gui);
1290 return LyX::ref().server();
1294 LyXServerSocket & theLyXServerSocket()
1296 // FIXME: this should not be use_gui dependent
1297 BOOST_ASSERT(use_gui);
1298 return LyX::ref().socket();
1302 kb_keymap & theTopLevelKeymap()
1304 BOOST_ASSERT(use_gui);
1305 return LyX::ref().topLevelKeymap();