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/LyXView.h"
49 #include "support/environment.h"
50 #include "support/filetools.h"
51 #include "support/fontutils.h"
52 #include "support/lyxlib.h"
53 #include "support/convert.h"
54 #include "support/os.h"
55 #include "support/package.h"
56 #include "support/path.h"
57 #include "support/systemcall.h"
59 #include <boost/bind.hpp>
60 #include <boost/filesystem/operations.hpp>
65 using lyx::support::addName;
66 using lyx::support::addPath;
67 using lyx::support::bformat;
68 using lyx::support::createDirectory;
69 using lyx::support::createLyXTmpDir;
70 using lyx::support::destroyDir;
71 using lyx::support::fileSearch;
72 using lyx::support::getEnv;
73 using lyx::support::i18nLibFileSearch;
74 using lyx::support::libFileSearch;
75 using lyx::support::package;
76 using lyx::support::prependEnvPath;
77 using lyx::support::rtrim;
78 using lyx::support::Systemcall;
82 namespace Alert = lyx::frontend::Alert;
83 namespace os = lyx::support::os;
84 namespace fs = boost::filesystem;
91 #ifndef CXX_GLOBAL_CSTD
98 lyx::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.
113 // Filled with the command line arguments "foo" of "-sysdir foo" or
115 string cl_system_support;
116 string cl_user_support;
119 void showFileError(string const & error)
121 Alert::warning(_("Could not read configuration file"),
122 bformat(_("Error while reading the configuration file\n%1$s.\n"
123 "Please check your installation."), lyx::from_utf8(error)));
127 void reconfigureUserLyXDir()
129 string const configure_command = package().configure_command();
131 lyxerr << lyx::to_utf8(_("LyX: reconfiguring user directory")) << endl;
132 lyx::support::Path p(package().user_support());
134 one.startscript(Systemcall::Wait, configure_command);
135 lyxerr << "LyX: " << lyx::to_utf8(_("Done!")) << endl;
141 /// The main application class private implementation.
142 struct LyX::Singletons
144 /// our function handler
147 BufferList buffer_list_;
149 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
151 boost::scoped_ptr<LyXServer> lyx_server_;
153 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
155 boost::scoped_ptr<lyx::frontend::Application> application_;
156 /// lyx session, containing lastfiles, lastfilepos, and lastopened
157 boost::scoped_ptr<lyx::Session> session_;
161 boost::scoped_ptr<LyX> LyX::singleton_;
164 int LyX::exec(int & argc, char * argv[])
166 BOOST_ASSERT(!singleton_.get());
167 // We must return from this before launching the gui so that
168 // other parts of the code can access singleton_ through
169 // LyX::ref and LyX::cref.
170 singleton_.reset(new LyX);
171 // Start the real execution loop.
172 return singleton_->priv_exec(argc, argv);
178 BOOST_ASSERT(singleton_.get());
179 return *singleton_.get();
183 LyX const & LyX::cref()
185 BOOST_ASSERT(singleton_.get());
186 return *singleton_.get();
191 : first_start(false), geometryOption_(false)
193 pimpl_.reset(new Singletons);
197 BufferList & LyX::bufferList()
199 return pimpl_->buffer_list_;
203 BufferList const & LyX::bufferList() const
205 return pimpl_->buffer_list_;
209 lyx::Session & LyX::session()
211 BOOST_ASSERT(pimpl_->session_.get());
212 return *pimpl_->session_.get();
216 lyx::Session const & LyX::session() const
218 BOOST_ASSERT(pimpl_->session_.get());
219 return *pimpl_->session_.get();
223 LyXFunc & LyX::lyxFunc()
225 return pimpl_->lyxfunc_;
229 LyXFunc const & LyX::lyxFunc() const
231 return pimpl_->lyxfunc_;
235 LyXServer & LyX::server()
237 BOOST_ASSERT(pimpl_->lyx_server_.get());
238 return *pimpl_->lyx_server_.get();
242 LyXServer const & LyX::server() const
244 BOOST_ASSERT(pimpl_->lyx_server_.get());
245 return *pimpl_->lyx_server_.get();
249 LyXServerSocket & LyX::socket()
251 BOOST_ASSERT(pimpl_->lyx_socket_.get());
252 return *pimpl_->lyx_socket_.get();
256 LyXServerSocket const & LyX::socket() const
258 BOOST_ASSERT(pimpl_->lyx_socket_.get());
259 return *pimpl_->lyx_socket_.get();
263 lyx::frontend::Application & LyX::application()
265 BOOST_ASSERT(pimpl_->application_.get());
266 return *pimpl_->application_.get();
270 lyx::frontend::Application const & LyX::application() const
272 BOOST_ASSERT(pimpl_->application_.get());
273 return *pimpl_->application_.get();
277 kb_keymap & LyX::topLevelKeymap()
279 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
280 return *pimpl_->toplevel_keymap_.get();
284 kb_keymap const & LyX::topLevelKeymap() const
286 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
287 return *pimpl_->toplevel_keymap_.get();
290 void LyX::addLyXView(LyXView * lyxview)
292 views_.push_back(lyxview);
296 Buffer const * const LyX::updateInset(InsetBase const * inset) const
301 Buffer const * buffer_ptr = 0;
302 ViewList::const_iterator it = views_.begin();
303 ViewList::const_iterator const end = views_.end();
304 for (; it != end; ++it) {
305 Buffer const * ptr = (*it)->updateInset(inset);
313 int LyX::priv_exec(int & argc, char * argv[])
315 // Here we need to parse the command line. At least
316 // we need to parse for "-dbg" and "-help"
317 easyParse(argc, argv);
319 lyx::support::init_package(argv[0], cl_system_support, cl_user_support,
320 lyx::support::top_build_dir_is_one_level_up);
322 vector<string> files;
323 int exit_status = execBatchCommands(argc, argv, files);
329 // Force adding of font path _before_ Application is initialized
330 lyx::support::addFontResources();
331 pimpl_->application_.reset(lyx::createApplication(argc, argv));
333 // FIXME: this global pointer should probably go.
334 theApp = pimpl_->application_.get();
335 restoreGuiSession(files);
336 // Start the real execution loop.
339 /* Create a CoreApplication class that will provide the main event loop and
340 * the socket callback registering. With Qt4, only QtCore library would be needed.
341 * When this done, a lyx::server_mode could be created and the following two
342 * line would be moved out from here.
344 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
345 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
346 lyx::support::os::internal_path(package().temp_dir() + "/lyxsocket")));
348 // handle the batch commands the user asked for
349 if (!batch_command.empty()) {
350 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
353 exit_status = pimpl_->application_->start(batch_command);
354 // Kill the application object before exiting. This avoid crash
356 pimpl_->application_.reset();
357 // Restore original font resources after Application is destroyed.
358 lyx::support::restoreFontResources();
361 // FIXME: create a ConsoleApplication
369 void LyX::prepareExit()
371 // Set a flag that we do quitting from the program,
372 // so no refreshes are necessary.
375 // close buffers first
376 pimpl_->buffer_list_.closeAll();
378 // do any other cleanup procedures now
379 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
381 if (!destroyDir(package().temp_dir())) {
382 docstring const msg =
383 bformat(_("Unable to remove the temporary directory %1$s"),
384 lyx::from_utf8(package().temp_dir()));
385 Alert::warning(_("Unable to remove temporary directory"), msg);
390 void LyX::earlyExit(int status)
392 BOOST_ASSERT(pimpl_->application_.get());
393 // LyX::pimpl_::application_ is not initialised at this
394 // point so it's safe to just exit after some cleanup.
400 void LyX::quit(bool noask)
402 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
405 if (!noask && !pimpl_->buffer_list_.quitWriteAll())
408 pimpl_->session_->writeFile();
414 pimpl_->lyx_server_.reset();
415 pimpl_->lyx_socket_.reset();
416 pimpl_->application_->exit(0);
421 int LyX::execBatchCommands(int & argc, char * argv[],
422 vector<string> & files)
424 // check for any spurious extra arguments
425 // other than documents
426 for (int argi = 1; argi < argc ; ++argi) {
427 if (argv[argi][0] == '-') {
428 lyxerr << lyx::to_utf8(
429 bformat(_("Wrong command line option `%1$s'. Exiting."),
430 lyx::from_utf8(argv[argi]))) << endl;
435 // Initialization of LyX (reads lyxrc and more)
436 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
437 bool success = init();
438 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
442 for (int argi = argc - 1; argi >= 1; --argi)
443 files.push_back(os::internal_path(argv[argi]));
446 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
448 // Execute batch commands if available
449 if (!batch_command.empty()) {
451 lyxerr[Debug::INIT] << "About to handle -x '"
452 << batch_command << '\'' << endl;
454 Buffer * last_loaded = 0;
456 vector<string>::const_iterator it = files.begin();
457 vector<string>::const_iterator end = files.end();
459 for (; it != end; ++it) {
460 // get absolute path of file and add ".lyx" to
461 // the filename if necessary
462 string s = fileSearch(string(), *it, "lyx");
464 Buffer * const b = newFile(*it, string(), true);
468 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
469 if (loadLyXFile(buf, s)) {
471 ErrorList const & el = buf->errorList("Parse");
473 for_each(el.begin(), el.end(),
474 boost::bind(&LyX::printError, this, _1));
477 pimpl_->buffer_list_.release(buf);
481 // try to dispatch to last loaded buffer first
484 if (last_loaded->dispatch(batch_command, &success)) {
489 files.clear(); // the files are already loaded
496 void LyX::restoreGuiSession(vector<string> const & files)
498 // determine windows size and position, from lyxrc and/or session
500 unsigned int width = 690;
501 unsigned int height = 510;
502 bool maximize = false;
504 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
505 width = lyxrc.geometry_width;
506 height = lyxrc.geometry_height;
508 // if lyxrc returns (0,0), then use session info
510 string val = session().loadSessionInfo("WindowWidth");
512 width = convert<unsigned int>(val);
513 val = session().loadSessionInfo("WindowHeight");
515 height = convert<unsigned int>(val);
516 if (session().loadSessionInfo("WindowIsMaximized") == "yes")
519 // if user wants to restore window position
522 if (lyxrc.geometry_xysaved) {
523 string val = session().loadSessionInfo("WindowPosX");
525 posx = convert<int>(val);
526 val = session().loadSessionInfo("WindowPosY");
528 posy = convert<int>(val);
531 if (geometryOption_) {
535 // create the main window
536 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
537 ref().addLyXView(view);
540 for_each(files.begin(), files.end(),
541 bind(&LyXView::loadLyXFile, view, _1, true));
543 // if a file is specified, I assume that user wants to edit *that* file
544 if (files.empty() && lyxrc.load_session) {
545 vector<string> const & lastopened = pimpl_->session_->lastOpenedFiles();
546 // do not add to the lastfile list since these files are restored from
547 // last seesion, and should be already there (regular files), or should
548 // not be added at all (help files).
549 for_each(lastopened.begin(), lastopened.end(),
550 bind(&LyXView::loadLyXFile, view, _1, false));
552 // clear this list to save a few bytes of RAM
553 pimpl_->session_->clearLastOpenedFiles();
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())
649 lyx::support::abort();
656 void LyX::printError(ErrorItem const & ei)
658 docstring tmp = _("LyX: ") + ei.error + lyx::char_type(':')
660 std::cerr << lyx::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 system_lyxrc = lyxrc;
732 system_formats = formats;
733 system_converters = converters;
734 system_movers = movers;
735 system_lcolor = lcolor;
737 // This one is edited through the preferences dialog.
738 if (!readRcFile("preferences"))
741 if (!readEncodingsFile("encodings"))
743 if (!readLanguagesFile("languages"))
747 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
753 pimpl_->toplevel_keymap_.reset(new kb_keymap);
754 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
755 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
757 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
760 if (!readUIFile(lyxrc.ui_file))
764 if (lyxerr.debugging(Debug::LYXRC))
767 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
768 if (!lyxrc.path_prefix.empty())
769 prependEnvPath("PATH", lyxrc.path_prefix);
771 if (fs::exists(lyxrc.document_path) &&
772 fs::is_directory(lyxrc.document_path))
773 package().document_dir() = lyxrc.document_path;
775 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
776 if (package().temp_dir().empty()) {
777 Alert::error(_("Could not create temporary directory"),
778 bformat(_("Could not create a temporary directory in\n"
779 "%1$s. Make sure that this\n"
780 "path exists and is writable and try again."),
781 lyx::from_utf8(lyxrc.tempdir_path)));
782 // createLyXTmpDir() tries sufficiently hard to create a
783 // usable temp dir, so the probability to come here is
784 // close to zero. We therefore don't try to overcome this
785 // problem with e.g. asking the user for a new path and
786 // trying again but simply exit.
790 if (lyxerr.debugging(Debug::INIT)) {
791 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
794 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
795 pimpl_->session_.reset(new lyx::Session(lyxrc.num_lastfiles));
800 void LyX::defaultKeyBindings(kb_keymap * kbmap)
802 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
803 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
804 kbmap->bind("Up", FuncRequest(LFUN_UP));
805 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
807 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
808 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
809 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
810 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
812 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
813 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
814 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
815 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
817 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
818 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
820 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
821 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
823 // kbmap->bindings to enable the use of the numeric keypad
825 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
826 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
827 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
828 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
829 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
830 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
831 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
832 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
833 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
834 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
835 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
836 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
837 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
838 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
839 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
840 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
841 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
842 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
843 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
844 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
845 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
846 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
847 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
848 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
852 void LyX::emergencyCleanup() const
854 // what to do about tmpfiles is non-obvious. we would
855 // like to delete any we find, but our lyxdir might
856 // contain documents etc. which might be helpful on
859 pimpl_->buffer_list_.emergencyWriteAll();
861 pimpl_->lyx_server_->emergencyCleanup();
862 pimpl_->lyx_server_.reset();
863 pimpl_->lyx_socket_.reset();
868 void LyX::deadKeyBindings(kb_keymap * kbmap)
870 // bindKeyings for transparent handling of deadkeys
871 // The keysyms are gotten from XFree86 X11R6
872 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
873 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
874 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
875 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
876 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
877 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
878 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
879 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
880 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
881 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
882 // nothing with this name
883 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
884 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
885 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
886 // nothing with this name either...
887 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
888 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
889 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
890 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
896 // return true if file does not exist or is older than configure.py.
897 bool needsUpdate(string const & file)
899 static string const configure_script =
900 addName(package().system_support(), "configure.py");
901 string const absfile =
902 addName(package().user_support(), file);
904 return (! fs::exists(absfile))
905 || (fs::last_write_time(configure_script)
906 > fs::last_write_time(absfile));
912 bool LyX::queryUserLyXDir(bool explicit_userdir)
914 // Does user directory exist?
915 if (fs::exists(package().user_support()) &&
916 fs::is_directory(package().user_support())) {
919 return needsUpdate("lyxrc.defaults")
920 || needsUpdate("textclass.lst")
921 || needsUpdate("packages.lst");
924 first_start = !explicit_userdir;
926 // If the user specified explicitly a directory, ask whether
927 // to create it. If the user says "no", then exit.
928 if (explicit_userdir &&
930 _("Missing user LyX directory"),
931 bformat(_("You have specified a non-existent user "
932 "LyX directory, %1$s.\n"
933 "It is needed to keep your own configuration."),
934 lyx::from_utf8(package().user_support())),
936 _("&Create directory"),
938 lyxerr << lyx::to_utf8(_("No user LyX directory. Exiting.")) << endl;
939 earlyExit(EXIT_FAILURE);
942 lyxerr << lyx::to_utf8(bformat(_("LyX: Creating directory %1$s"),
943 lyx::from_utf8(package().user_support())))
946 if (!createDirectory(package().user_support(), 0755)) {
947 // Failed, so let's exit.
948 lyxerr << lyx::to_utf8(_("Failed to create directory. Exiting."))
950 earlyExit(EXIT_FAILURE);
957 bool LyX::readRcFile(string const & name)
959 lyxerr[Debug::INIT] << "About to read " << name << "... ";
961 string const lyxrc_path = libFileSearch(string(), name);
962 if (!lyxrc_path.empty()) {
964 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
966 if (lyxrc.read(lyxrc_path) < 0) {
971 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
977 // Read the ui file `name'
978 bool LyX::readUIFile(string const & name)
988 struct keyword_item uitags[ui_last - 1] = {
989 { "include", ui_include },
990 { "menuset", ui_menuset },
991 { "toolbar", ui_toolbar },
992 { "toolbars", ui_toolbars }
995 // Ensure that a file is read only once (prevents include loops)
996 static std::list<string> uifiles;
997 std::list<string>::const_iterator it = uifiles.begin();
998 std::list<string>::const_iterator end = uifiles.end();
999 it = std::find(it, end, name);
1001 lyxerr[Debug::INIT] << "UI file '" << name
1002 << "' has been read already. "
1003 << "Is this an include loop?"
1008 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1010 string const ui_path = libFileSearch("ui", name, "ui");
1012 if (ui_path.empty()) {
1013 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1014 showFileError(name);
1017 uifiles.push_back(name);
1019 lyxerr[Debug::INIT] << "Found " << name
1020 << " in " << ui_path << endl;
1021 LyXLex lex(uitags, ui_last - 1);
1022 lex.setFile(ui_path);
1024 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1028 if (lyxerr.debugging(Debug::PARSER))
1029 lex.printTable(lyxerr);
1031 while (lex.isOK()) {
1032 switch (lex.lex()) {
1035 string const file = lex.getString();
1036 if (!readUIFile(file))
1041 menubackend.read(lex);
1045 toolbarbackend.read(lex);
1049 toolbarbackend.readToolbars(lex);
1053 if (!rtrim(lex.getString()).empty())
1054 lex.printError("LyX::ReadUIFile: "
1055 "Unknown menu tag: `$$Token'");
1063 // Read the languages file `name'
1064 bool LyX::readLanguagesFile(string const & name)
1066 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1068 string const lang_path = libFileSearch(string(), name);
1069 if (lang_path.empty()) {
1070 showFileError(name);
1073 languages.read(lang_path);
1078 // Read the encodings file `name'
1079 bool LyX::readEncodingsFile(string const & name)
1081 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1083 string const enc_path = libFileSearch(string(), name);
1084 if (enc_path.empty()) {
1085 showFileError(name);
1088 encodings.read(enc_path);
1097 /// return the the number of arguments consumed
1098 typedef boost::function<int(string const &, string const &)> cmd_helper;
1100 int parse_dbg(string const & arg, string const &)
1103 lyxerr << lyx::to_utf8(_("List of supported debug flags:")) << endl;
1104 Debug::showTags(lyxerr);
1107 lyxerr << lyx::to_utf8(bformat(_("Setting debug level to %1$s"), lyx::from_utf8(arg))) << endl;
1109 lyxerr.level(Debug::value(arg));
1110 Debug::showLevel(lyxerr, lyxerr.level());
1115 int parse_help(string const &, string const &)
1118 lyx::to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1119 "Command line switches (case sensitive):\n"
1120 "\t-help summarize LyX usage\n"
1121 "\t-userdir dir set user directory to dir\n"
1122 "\t-sysdir dir set system directory to dir\n"
1123 "\t-geometry WxH+X+Y set geometry of the main window\n"
1124 "\t-dbg feature[,feature]...\n"
1125 " select the features to debug.\n"
1126 " Type `lyx -dbg' to see the list of features\n"
1127 "\t-x [--execute] command\n"
1128 " where command is a lyx command.\n"
1129 "\t-e [--export] fmt\n"
1130 " where fmt is the export format of choice.\n"
1131 "\t-i [--import] fmt file.xxx\n"
1132 " where fmt is the import format of choice\n"
1133 " and file.xxx is the file to be imported.\n"
1134 "\t-version summarize version and build info\n"
1135 "Check the LyX man page for more details.")) << endl;
1140 int parse_version(string const &, string const &)
1142 lyxerr << "LyX " << lyx_version
1143 << " (" << lyx_release_date << ")" << endl;
1144 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1146 lyxerr << lyx_version_info << endl;
1151 int parse_sysdir(string const & arg, string const &)
1154 lyxerr << lyx::to_utf8(_("Missing directory for -sysdir switch")) << endl;
1157 cl_system_support = arg;
1161 int parse_userdir(string const & arg, string const &)
1164 lyxerr << lyx::to_utf8(_("Missing directory for -userdir switch")) << endl;
1167 cl_user_support = arg;
1171 int parse_execute(string const & arg, string const &)
1174 lyxerr << lyx::to_utf8(_("Missing command string after --execute switch")) << endl;
1181 int parse_export(string const & type, string const &)
1184 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1185 "--export switch")) << endl;
1188 batch = "buffer-export " + type;
1189 lyx::use_gui = false;
1193 int parse_import(string const & type, string const & file)
1196 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1197 "--import switch")) << endl;
1201 lyxerr << lyx::to_utf8(_("Missing filename for --import")) << endl;
1205 batch = "buffer-import " + type + ' ' + file;
1212 void LyX::easyParse(int & argc, char * argv[])
1214 std::map<string, cmd_helper> cmdmap;
1216 cmdmap["-dbg"] = parse_dbg;
1217 cmdmap["-help"] = parse_help;
1218 cmdmap["--help"] = parse_help;
1219 cmdmap["-version"] = parse_version;
1220 cmdmap["--version"] = parse_version;
1221 cmdmap["-sysdir"] = parse_sysdir;
1222 cmdmap["-userdir"] = parse_userdir;
1223 cmdmap["-x"] = parse_execute;
1224 cmdmap["--execute"] = parse_execute;
1225 cmdmap["-e"] = parse_export;
1226 cmdmap["--export"] = parse_export;
1227 cmdmap["-i"] = parse_import;
1228 cmdmap["--import"] = parse_import;
1230 for (int i = 1; i < argc; ++i) {
1231 std::map<string, cmd_helper>::const_iterator it
1232 = cmdmap.find(argv[i]);
1234 // check for X11 -geometry option
1235 if (lyx::support::compare(argv[i], "-geometry") == 0)
1236 geometryOption_ = true;
1238 // don't complain if not found - may be parsed later
1239 if (it == cmdmap.end())
1242 string arg((i + 1 < argc) ? argv[i + 1] : "");
1243 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1245 int const remove = 1 + it->second(arg, arg2);
1247 // Now, remove used arguments by shifting
1248 // the following ones remove places down.
1250 for (int j = i; j < argc; ++j)
1251 argv[j] = argv[j + remove];
1255 batch_command = batch;
1260 FuncStatus getStatus(FuncRequest const & action)
1262 return LyX::ref().lyxFunc().getStatus(action);
1266 void dispatch(FuncRequest const & action)
1268 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(lyx::use_gui);
1290 return LyX::ref().server();
1294 LyXServerSocket & theLyXServerSocket()
1296 // FIXME: this should not be use_gui dependent
1297 BOOST_ASSERT(lyx::use_gui);
1298 return LyX::ref().socket();
1302 kb_keymap & theTopLevelKeymap()
1304 BOOST_ASSERT(lyx::use_gui);
1305 return LyX::ref().topLevelKeymap();