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_->application_->exit(0);
419 int LyX::execBatchCommands(int & argc, char * argv[],
420 vector<string> & files)
422 // check for any spurious extra arguments
423 // other than documents
424 for (int argi = 1; argi < argc ; ++argi) {
425 if (argv[argi][0] == '-') {
426 lyxerr << lyx::to_utf8(
427 bformat(_("Wrong command line option `%1$s'. Exiting."),
428 lyx::from_utf8(argv[argi]))) << endl;
433 // Initialization of LyX (reads lyxrc and more)
434 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
435 bool success = init();
436 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
440 for (int argi = argc - 1; argi >= 1; --argi)
441 files.push_back(os::internal_path(argv[argi]));
444 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
446 // Execute batch commands if available
447 if (!batch_command.empty()) {
449 lyxerr[Debug::INIT] << "About to handle -x '"
450 << batch_command << '\'' << endl;
452 Buffer * last_loaded = 0;
454 vector<string>::const_iterator it = files.begin();
455 vector<string>::const_iterator end = files.end();
457 for (; it != end; ++it) {
458 // get absolute path of file and add ".lyx" to
459 // the filename if necessary
460 string s = fileSearch(string(), *it, "lyx");
462 Buffer * const b = newFile(*it, string(), true);
466 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
467 if (loadLyXFile(buf, s)) {
469 ErrorList const & el = buf->errorList("Parse");
471 for_each(el.begin(), el.end(),
472 boost::bind(&LyX::printError, this, _1));
475 pimpl_->buffer_list_.release(buf);
479 // try to dispatch to last loaded buffer first
482 if (last_loaded->dispatch(batch_command, &success)) {
487 files.clear(); // the files are already loaded
494 void LyX::restoreGuiSession(vector<string> const & files)
496 // determine windows size and position, from lyxrc and/or session
498 unsigned int width = 690;
499 unsigned int height = 510;
500 bool maximize = false;
502 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
503 width = lyxrc.geometry_width;
504 height = lyxrc.geometry_height;
506 // if lyxrc returns (0,0), then use session info
508 string val = session().loadSessionInfo("WindowWidth");
510 width = convert<unsigned int>(val);
511 val = session().loadSessionInfo("WindowHeight");
513 height = convert<unsigned int>(val);
514 if (session().loadSessionInfo("WindowIsMaximized") == "yes")
517 // if user wants to restore window position
520 if (lyxrc.geometry_xysaved) {
521 string val = session().loadSessionInfo("WindowPosX");
523 posx = convert<int>(val);
524 val = session().loadSessionInfo("WindowPosY");
526 posy = convert<int>(val);
529 if (geometryOption_) {
533 // create the main window
534 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
535 ref().addLyXView(view);
538 for_each(files.begin(), files.end(),
539 bind(&LyXView::loadLyXFile, view, _1, true));
541 // if a file is specified, I assume that user wants to edit *that* file
542 if (files.empty() && lyxrc.load_session) {
543 vector<string> const & lastopened = pimpl_->session_->lastOpenedFiles();
544 // do not add to the lastfile list since these files are restored from
545 // last seesion, and should be already there (regular files), or should
546 // not be added at all (help files).
547 for_each(lastopened.begin(), lastopened.end(),
548 bind(&LyXView::loadLyXFile, view, _1, false));
550 // clear this list to save a few bytes of RAM
551 pimpl_->session_->clearLastOpenedFiles();
558 The SIGHUP signal does not exist on Windows and does not need to be handled.
560 Windows handles SIGFPE and SIGSEGV signals as expected.
562 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
563 cause a new thread to be spawned. This may well result in unexpected
564 behaviour by the single-threaded LyX.
566 SIGTERM signals will come only from another process actually sending
567 that signal using 'raise' in Windows' POSIX compatability layer. It will
568 not come from the general "terminate process" methods that everyone
569 actually uses (and which can't be trapped). Killing an app 'politely' on
570 Windows involves first sending a WM_CLOSE message, something that is
571 caught already by the Qt frontend.
573 For more information see:
575 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
576 ...signals are mostly useless on Windows for a variety of reasons that are
579 'UNIX Application Migration Guide, Chapter 9'
580 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
582 'How To Terminate an Application "Cleanly" in Win32'
583 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
587 static void error_handler(int err_sig)
589 // Throw away any signals other than the first one received.
590 static sig_atomic_t handling_error = false;
593 handling_error = true;
595 // We have received a signal indicating a fatal error, so
596 // try and save the data ASAP.
597 LyX::cref().emergencyCleanup();
599 // These lyxerr calls may or may not work:
601 // Signals are asynchronous, so the main program may be in a very
602 // fragile state when a signal is processed and thus while a signal
603 // handler function executes.
604 // In general, therefore, we should avoid performing any
605 // I/O operations or calling most library and system functions from
608 // This shouldn't matter here, however, as we've already invoked
613 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
617 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
620 lyxerr << "\nlyx: SIGSEGV signal caught\n"
621 "Sorry, you have found a bug in LyX. "
622 "Please read the bug-reporting instructions "
623 "in Help->Introduction and send us a bug report, "
624 "if necessary. Thanks !\nBye." << endl;
632 // Deinstall the signal handlers
634 signal(SIGHUP, SIG_DFL);
636 signal(SIGINT, SIG_DFL);
637 signal(SIGFPE, SIG_DFL);
638 signal(SIGSEGV, SIG_DFL);
639 signal(SIGTERM, SIG_DFL);
642 if (err_sig == SIGSEGV ||
643 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
645 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
647 lyx::support::abort();
654 void LyX::printError(ErrorItem const & ei)
656 docstring tmp = _("LyX: ") + ei.error + lyx::char_type(':')
658 std::cerr << lyx::to_utf8(tmp) << std::endl;
662 void LyX::initGuiFont()
664 if (lyxrc.roman_font_name.empty())
665 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
667 if (lyxrc.sans_font_name.empty())
668 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
670 if (lyxrc.typewriter_font_name.empty())
671 lyxrc.typewriter_font_name
672 = pimpl_->application_->typewriterFontName();
679 signal(SIGHUP, error_handler);
681 signal(SIGFPE, error_handler);
682 signal(SIGSEGV, error_handler);
683 signal(SIGINT, error_handler);
684 signal(SIGTERM, error_handler);
685 // SIGPIPE can be safely ignored.
687 lyxrc.tempdir_path = package().temp_dir();
688 lyxrc.document_path = package().document_dir();
690 if (lyxrc.template_path.empty()) {
691 lyxrc.template_path = addPath(package().system_support(),
696 // Read configuration files
699 // This one may have been distributed along with LyX.
700 if (!readRcFile("lyxrc.dist"))
703 // Set the PATH correctly.
704 #if !defined (USE_POSIX_PACKAGING)
705 // Add the directory containing the LyX executable to the path
706 // so that LyX can find things like tex2lyx.
707 if (package().build_support().empty())
708 prependEnvPath("PATH", package().binary_dir());
710 if (!lyxrc.path_prefix.empty())
711 prependEnvPath("PATH", lyxrc.path_prefix);
713 // Check that user LyX directory is ok.
714 if (queryUserLyXDir(package().explicit_user_support()))
715 reconfigureUserLyXDir();
717 // no need for a splash when there is no GUI
722 // This one is generated in user_support directory by lib/configure.py.
723 if (!readRcFile("lyxrc.defaults"))
726 // Query the OS to know what formats are viewed natively
727 formats.setAutoOpen();
729 system_lyxrc = lyxrc;
730 system_formats = formats;
731 system_converters = converters;
732 system_movers = movers;
733 system_lcolor = lcolor;
735 // This one is edited through the preferences dialog.
736 if (!readRcFile("preferences"))
739 if (!readEncodingsFile("encodings"))
741 if (!readLanguagesFile("languages"))
745 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
751 pimpl_->toplevel_keymap_.reset(new kb_keymap);
752 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
753 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
755 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
758 if (!readUIFile(lyxrc.ui_file))
762 if (lyxerr.debugging(Debug::LYXRC))
765 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
766 if (!lyxrc.path_prefix.empty())
767 prependEnvPath("PATH", lyxrc.path_prefix);
769 if (fs::exists(lyxrc.document_path) &&
770 fs::is_directory(lyxrc.document_path))
771 package().document_dir() = lyxrc.document_path;
773 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
774 if (package().temp_dir().empty()) {
775 Alert::error(_("Could not create temporary directory"),
776 bformat(_("Could not create a temporary directory in\n"
777 "%1$s. Make sure that this\n"
778 "path exists and is writable and try again."),
779 lyx::from_utf8(lyxrc.tempdir_path)));
780 // createLyXTmpDir() tries sufficiently hard to create a
781 // usable temp dir, so the probability to come here is
782 // close to zero. We therefore don't try to overcome this
783 // problem with e.g. asking the user for a new path and
784 // trying again but simply exit.
788 if (lyxerr.debugging(Debug::INIT)) {
789 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
792 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
793 pimpl_->session_.reset(new lyx::Session(lyxrc.num_lastfiles));
798 void LyX::defaultKeyBindings(kb_keymap * kbmap)
800 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
801 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
802 kbmap->bind("Up", FuncRequest(LFUN_UP));
803 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
805 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
806 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
807 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
808 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
810 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
811 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
812 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
813 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
815 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
816 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
818 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
819 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
821 // kbmap->bindings to enable the use of the numeric keypad
823 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
824 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
825 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
826 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
827 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
828 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
829 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
830 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
831 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
832 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
833 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
834 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
835 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
836 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
837 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
838 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
839 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
840 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
841 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
842 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
843 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
844 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
845 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
846 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
850 void LyX::emergencyCleanup() const
852 // what to do about tmpfiles is non-obvious. we would
853 // like to delete any we find, but our lyxdir might
854 // contain documents etc. which might be helpful on
857 pimpl_->buffer_list_.emergencyWriteAll();
859 pimpl_->lyx_server_->emergencyCleanup();
863 void LyX::deadKeyBindings(kb_keymap * kbmap)
865 // bindKeyings for transparent handling of deadkeys
866 // The keysyms are gotten from XFree86 X11R6
867 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
868 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
869 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
870 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
871 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
872 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
873 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
874 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
875 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
876 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
877 // nothing with this name
878 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
879 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
880 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
881 // nothing with this name either...
882 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
883 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
884 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
885 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
891 // return true if file does not exist or is older than configure.py.
892 bool needsUpdate(string const & file)
894 static string const configure_script =
895 addName(package().system_support(), "configure.py");
896 string const absfile =
897 addName(package().user_support(), file);
899 return (! fs::exists(absfile))
900 || (fs::last_write_time(configure_script)
901 > fs::last_write_time(absfile));
907 bool LyX::queryUserLyXDir(bool explicit_userdir)
909 // Does user directory exist?
910 if (fs::exists(package().user_support()) &&
911 fs::is_directory(package().user_support())) {
914 return needsUpdate("lyxrc.defaults")
915 || needsUpdate("textclass.lst")
916 || needsUpdate("packages.lst");
919 first_start = !explicit_userdir;
921 // If the user specified explicitly a directory, ask whether
922 // to create it. If the user says "no", then exit.
923 if (explicit_userdir &&
925 _("Missing user LyX directory"),
926 bformat(_("You have specified a non-existent user "
927 "LyX directory, %1$s.\n"
928 "It is needed to keep your own configuration."),
929 lyx::from_utf8(package().user_support())),
931 _("&Create directory"),
933 lyxerr << lyx::to_utf8(_("No user LyX directory. Exiting.")) << endl;
934 earlyExit(EXIT_FAILURE);
937 lyxerr << lyx::to_utf8(bformat(_("LyX: Creating directory %1$s"),
938 lyx::from_utf8(package().user_support())))
941 if (!createDirectory(package().user_support(), 0755)) {
942 // Failed, so let's exit.
943 lyxerr << lyx::to_utf8(_("Failed to create directory. Exiting."))
945 earlyExit(EXIT_FAILURE);
952 bool LyX::readRcFile(string const & name)
954 lyxerr[Debug::INIT] << "About to read " << name << "... ";
956 string const lyxrc_path = libFileSearch(string(), name);
957 if (!lyxrc_path.empty()) {
959 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
961 if (lyxrc.read(lyxrc_path) < 0) {
966 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
972 // Read the ui file `name'
973 bool LyX::readUIFile(string const & name)
983 struct keyword_item uitags[ui_last - 1] = {
984 { "include", ui_include },
985 { "menuset", ui_menuset },
986 { "toolbar", ui_toolbar },
987 { "toolbars", ui_toolbars }
990 // Ensure that a file is read only once (prevents include loops)
991 static std::list<string> uifiles;
992 std::list<string>::const_iterator it = uifiles.begin();
993 std::list<string>::const_iterator end = uifiles.end();
994 it = std::find(it, end, name);
996 lyxerr[Debug::INIT] << "UI file '" << name
997 << "' has been read already. "
998 << "Is this an include loop?"
1003 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1005 string const ui_path = libFileSearch("ui", name, "ui");
1007 if (ui_path.empty()) {
1008 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1009 showFileError(name);
1012 uifiles.push_back(name);
1014 lyxerr[Debug::INIT] << "Found " << name
1015 << " in " << ui_path << endl;
1016 LyXLex lex(uitags, ui_last - 1);
1017 lex.setFile(ui_path);
1019 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1023 if (lyxerr.debugging(Debug::PARSER))
1024 lex.printTable(lyxerr);
1026 while (lex.isOK()) {
1027 switch (lex.lex()) {
1030 string const file = lex.getString();
1031 if (!readUIFile(file))
1036 menubackend.read(lex);
1040 toolbarbackend.read(lex);
1044 toolbarbackend.readToolbars(lex);
1048 if (!rtrim(lex.getString()).empty())
1049 lex.printError("LyX::ReadUIFile: "
1050 "Unknown menu tag: `$$Token'");
1058 // Read the languages file `name'
1059 bool LyX::readLanguagesFile(string const & name)
1061 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1063 string const lang_path = libFileSearch(string(), name);
1064 if (lang_path.empty()) {
1065 showFileError(name);
1068 languages.read(lang_path);
1073 // Read the encodings file `name'
1074 bool LyX::readEncodingsFile(string const & name)
1076 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1078 string const enc_path = libFileSearch(string(), name);
1079 if (enc_path.empty()) {
1080 showFileError(name);
1083 encodings.read(enc_path);
1092 /// return the the number of arguments consumed
1093 typedef boost::function<int(string const &, string const &)> cmd_helper;
1095 int parse_dbg(string const & arg, string const &)
1098 lyxerr << lyx::to_utf8(_("List of supported debug flags:")) << endl;
1099 Debug::showTags(lyxerr);
1102 lyxerr << lyx::to_utf8(bformat(_("Setting debug level to %1$s"), lyx::from_utf8(arg))) << endl;
1104 lyxerr.level(Debug::value(arg));
1105 Debug::showLevel(lyxerr, lyxerr.level());
1110 int parse_help(string const &, string const &)
1113 lyx::to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1114 "Command line switches (case sensitive):\n"
1115 "\t-help summarize LyX usage\n"
1116 "\t-userdir dir set user directory to dir\n"
1117 "\t-sysdir dir set system directory to dir\n"
1118 "\t-geometry WxH+X+Y set geometry of the main window\n"
1119 "\t-dbg feature[,feature]...\n"
1120 " select the features to debug.\n"
1121 " Type `lyx -dbg' to see the list of features\n"
1122 "\t-x [--execute] command\n"
1123 " where command is a lyx command.\n"
1124 "\t-e [--export] fmt\n"
1125 " where fmt is the export format of choice.\n"
1126 "\t-i [--import] fmt file.xxx\n"
1127 " where fmt is the import format of choice\n"
1128 " and file.xxx is the file to be imported.\n"
1129 "\t-version summarize version and build info\n"
1130 "Check the LyX man page for more details.")) << endl;
1135 int parse_version(string const &, string const &)
1137 lyxerr << "LyX " << lyx_version
1138 << " (" << lyx_release_date << ")" << endl;
1139 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1141 lyxerr << lyx_version_info << endl;
1146 int parse_sysdir(string const & arg, string const &)
1149 lyxerr << lyx::to_utf8(_("Missing directory for -sysdir switch")) << endl;
1152 cl_system_support = arg;
1156 int parse_userdir(string const & arg, string const &)
1159 lyxerr << lyx::to_utf8(_("Missing directory for -userdir switch")) << endl;
1162 cl_user_support = arg;
1166 int parse_execute(string const & arg, string const &)
1169 lyxerr << lyx::to_utf8(_("Missing command string after --execute switch")) << endl;
1176 int parse_export(string const & type, string const &)
1179 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1180 "--export switch")) << endl;
1183 batch = "buffer-export " + type;
1184 lyx::use_gui = false;
1188 int parse_import(string const & type, string const & file)
1191 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1192 "--import switch")) << endl;
1196 lyxerr << lyx::to_utf8(_("Missing filename for --import")) << endl;
1200 batch = "buffer-import " + type + ' ' + file;
1207 void LyX::easyParse(int & argc, char * argv[])
1209 std::map<string, cmd_helper> cmdmap;
1211 cmdmap["-dbg"] = parse_dbg;
1212 cmdmap["-help"] = parse_help;
1213 cmdmap["--help"] = parse_help;
1214 cmdmap["-version"] = parse_version;
1215 cmdmap["--version"] = parse_version;
1216 cmdmap["-sysdir"] = parse_sysdir;
1217 cmdmap["-userdir"] = parse_userdir;
1218 cmdmap["-x"] = parse_execute;
1219 cmdmap["--execute"] = parse_execute;
1220 cmdmap["-e"] = parse_export;
1221 cmdmap["--export"] = parse_export;
1222 cmdmap["-i"] = parse_import;
1223 cmdmap["--import"] = parse_import;
1225 for (int i = 1; i < argc; ++i) {
1226 std::map<string, cmd_helper>::const_iterator it
1227 = cmdmap.find(argv[i]);
1229 // check for X11 -geometry option
1230 if (lyx::support::compare(argv[i], "-geometry") == 0)
1231 geometryOption_ = true;
1233 // don't complain if not found - may be parsed later
1234 if (it == cmdmap.end())
1237 string arg((i + 1 < argc) ? argv[i + 1] : "");
1238 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1240 int const remove = 1 + it->second(arg, arg2);
1242 // Now, remove used arguments by shifting
1243 // the following ones remove places down.
1245 for (int j = i; j < argc; ++j)
1246 argv[j] = argv[j + remove];
1250 batch_command = batch;
1255 FuncStatus getStatus(FuncRequest const & action)
1257 return LyX::ref().lyxFunc().getStatus(action);
1261 void dispatch(FuncRequest const & action)
1263 LyX::ref().lyxFunc().dispatch(action);
1269 BufferList & theBufferList()
1271 return LyX::ref().bufferList();
1275 LyXFunc & theLyXFunc()
1277 return LyX::ref().lyxFunc();
1281 LyXServer & theLyXServer()
1283 // FIXME: this should not be use_gui dependent
1284 BOOST_ASSERT(lyx::use_gui);
1285 return LyX::ref().server();
1289 LyXServerSocket & theLyXServerSocket()
1291 // FIXME: this should not be use_gui dependent
1292 BOOST_ASSERT(lyx::use_gui);
1293 return LyX::ref().socket();
1297 kb_keymap & theTopLevelKeymap()
1299 BOOST_ASSERT(lyx::use_gui);
1300 return LyX::ref().topLevelKeymap();