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);
388 pimpl_->lyx_server_.reset();
389 pimpl_->lyx_socket_.reset();
393 void LyX::earlyExit(int status)
395 BOOST_ASSERT(pimpl_->application_.get());
396 // LyX::pimpl_::application_ is not initialised at this
397 // point so it's safe to just exit after some cleanup.
403 void LyX::quit(bool noask)
405 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
408 if (!noask && !pimpl_->buffer_list_.quitWriteAll())
411 pimpl_->session_->writeFile();
417 pimpl_->application_->exit(0);
422 int LyX::execBatchCommands(int & argc, char * argv[],
423 vector<string> & files)
425 // check for any spurious extra arguments
426 // other than documents
427 for (int argi = 1; argi < argc ; ++argi) {
428 if (argv[argi][0] == '-') {
429 lyxerr << lyx::to_utf8(
430 bformat(_("Wrong command line option `%1$s'. Exiting."),
431 lyx::from_utf8(argv[argi]))) << endl;
436 // Initialization of LyX (reads lyxrc and more)
437 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
438 bool success = init();
439 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
443 for (int argi = argc - 1; argi >= 1; --argi)
444 files.push_back(os::internal_path(argv[argi]));
447 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
449 // Execute batch commands if available
450 if (!batch_command.empty()) {
452 lyxerr[Debug::INIT] << "About to handle -x '"
453 << batch_command << '\'' << endl;
455 Buffer * last_loaded = 0;
457 vector<string>::const_iterator it = files.begin();
458 vector<string>::const_iterator end = files.end();
460 for (; it != end; ++it) {
461 // get absolute path of file and add ".lyx" to
462 // the filename if necessary
463 string s = fileSearch(string(), *it, "lyx");
465 Buffer * const b = newFile(*it, string(), true);
469 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
470 if (loadLyXFile(buf, s)) {
472 ErrorList const & el = buf->errorList("Parse");
474 for_each(el.begin(), el.end(),
475 boost::bind(&LyX::printError, this, _1));
478 pimpl_->buffer_list_.release(buf);
482 // try to dispatch to last loaded buffer first
485 if (last_loaded->dispatch(batch_command, &success)) {
490 files.clear(); // the files are already loaded
497 void LyX::restoreGuiSession(vector<string> const & files)
499 // determine windows size and position, from lyxrc and/or session
501 unsigned int width = 690;
502 unsigned int height = 510;
503 bool maximize = false;
505 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
506 width = lyxrc.geometry_width;
507 height = lyxrc.geometry_height;
509 // if lyxrc returns (0,0), then use session info
511 string val = session().loadSessionInfo("WindowWidth");
513 width = convert<unsigned int>(val);
514 val = session().loadSessionInfo("WindowHeight");
516 height = convert<unsigned int>(val);
517 if (session().loadSessionInfo("WindowIsMaximized") == "yes")
520 // if user wants to restore window position
523 if (lyxrc.geometry_xysaved) {
524 string val = session().loadSessionInfo("WindowPosX");
526 posx = convert<int>(val);
527 val = session().loadSessionInfo("WindowPosY");
529 posy = convert<int>(val);
532 if (geometryOption_) {
536 // create the main window
537 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
538 ref().addLyXView(view);
541 for_each(files.begin(), files.end(),
542 bind(&LyXView::loadLyXFile, view, _1, true));
544 // if a file is specified, I assume that user wants to edit *that* file
545 if (files.empty() && lyxrc.load_session) {
546 vector<string> const & lastopened = pimpl_->session_->lastOpenedFiles();
547 // do not add to the lastfile list since these files are restored from
548 // last seesion, and should be already there (regular files), or should
549 // not be added at all (help files).
550 for_each(lastopened.begin(), lastopened.end(),
551 bind(&LyXView::loadLyXFile, view, _1, false));
553 // clear this list to save a few bytes of RAM
554 pimpl_->session_->clearLastOpenedFiles();
561 The SIGHUP signal does not exist on Windows and does not need to be handled.
563 Windows handles SIGFPE and SIGSEGV signals as expected.
565 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
566 cause a new thread to be spawned. This may well result in unexpected
567 behaviour by the single-threaded LyX.
569 SIGTERM signals will come only from another process actually sending
570 that signal using 'raise' in Windows' POSIX compatability layer. It will
571 not come from the general "terminate process" methods that everyone
572 actually uses (and which can't be trapped). Killing an app 'politely' on
573 Windows involves first sending a WM_CLOSE message, something that is
574 caught already by the Qt frontend.
576 For more information see:
578 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
579 ...signals are mostly useless on Windows for a variety of reasons that are
582 'UNIX Application Migration Guide, Chapter 9'
583 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
585 'How To Terminate an Application "Cleanly" in Win32'
586 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
590 static void error_handler(int err_sig)
592 // Throw away any signals other than the first one received.
593 static sig_atomic_t handling_error = false;
596 handling_error = true;
598 // We have received a signal indicating a fatal error, so
599 // try and save the data ASAP.
600 LyX::cref().emergencyCleanup();
602 // These lyxerr calls may or may not work:
604 // Signals are asynchronous, so the main program may be in a very
605 // fragile state when a signal is processed and thus while a signal
606 // handler function executes.
607 // In general, therefore, we should avoid performing any
608 // I/O operations or calling most library and system functions from
611 // This shouldn't matter here, however, as we've already invoked
616 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
620 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
623 lyxerr << "\nlyx: SIGSEGV signal caught\n"
624 "Sorry, you have found a bug in LyX. "
625 "Please read the bug-reporting instructions "
626 "in Help->Introduction and send us a bug report, "
627 "if necessary. Thanks !\nBye." << endl;
635 // Deinstall the signal handlers
637 signal(SIGHUP, SIG_DFL);
639 signal(SIGINT, SIG_DFL);
640 signal(SIGFPE, SIG_DFL);
641 signal(SIGSEGV, SIG_DFL);
642 signal(SIGTERM, SIG_DFL);
645 if (err_sig == SIGSEGV ||
646 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
648 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
650 lyx::support::abort();
657 void LyX::printError(ErrorItem const & ei)
659 docstring tmp = _("LyX: ") + ei.error + lyx::char_type(':')
661 std::cerr << lyx::to_utf8(tmp) << std::endl;
665 void LyX::initGuiFont()
667 if (lyxrc.roman_font_name.empty())
668 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
670 if (lyxrc.sans_font_name.empty())
671 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
673 if (lyxrc.typewriter_font_name.empty())
674 lyxrc.typewriter_font_name
675 = pimpl_->application_->typewriterFontName();
682 signal(SIGHUP, error_handler);
684 signal(SIGFPE, error_handler);
685 signal(SIGSEGV, error_handler);
686 signal(SIGINT, error_handler);
687 signal(SIGTERM, error_handler);
688 // SIGPIPE can be safely ignored.
690 lyxrc.tempdir_path = package().temp_dir();
691 lyxrc.document_path = package().document_dir();
693 if (lyxrc.template_path.empty()) {
694 lyxrc.template_path = addPath(package().system_support(),
699 // Read configuration files
702 // This one may have been distributed along with LyX.
703 if (!readRcFile("lyxrc.dist"))
706 // Set the PATH correctly.
707 #if !defined (USE_POSIX_PACKAGING)
708 // Add the directory containing the LyX executable to the path
709 // so that LyX can find things like tex2lyx.
710 if (package().build_support().empty())
711 prependEnvPath("PATH", package().binary_dir());
713 if (!lyxrc.path_prefix.empty())
714 prependEnvPath("PATH", lyxrc.path_prefix);
716 // Check that user LyX directory is ok.
717 if (queryUserLyXDir(package().explicit_user_support()))
718 reconfigureUserLyXDir();
720 // no need for a splash when there is no GUI
725 // This one is generated in user_support directory by lib/configure.py.
726 if (!readRcFile("lyxrc.defaults"))
729 // Query the OS to know what formats are viewed natively
730 formats.setAutoOpen();
732 system_lyxrc = lyxrc;
733 system_formats = formats;
734 system_converters = converters;
735 system_movers = movers;
736 system_lcolor = lcolor;
738 // This one is edited through the preferences dialog.
739 if (!readRcFile("preferences"))
742 if (!readEncodingsFile("encodings"))
744 if (!readLanguagesFile("languages"))
748 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
754 pimpl_->toplevel_keymap_.reset(new kb_keymap);
755 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
756 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
758 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
761 if (!readUIFile(lyxrc.ui_file))
765 if (lyxerr.debugging(Debug::LYXRC))
768 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
769 if (!lyxrc.path_prefix.empty())
770 prependEnvPath("PATH", lyxrc.path_prefix);
772 if (fs::exists(lyxrc.document_path) &&
773 fs::is_directory(lyxrc.document_path))
774 package().document_dir() = lyxrc.document_path;
776 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
777 if (package().temp_dir().empty()) {
778 Alert::error(_("Could not create temporary directory"),
779 bformat(_("Could not create a temporary directory in\n"
780 "%1$s. Make sure that this\n"
781 "path exists and is writable and try again."),
782 lyx::from_utf8(lyxrc.tempdir_path)));
783 // createLyXTmpDir() tries sufficiently hard to create a
784 // usable temp dir, so the probability to come here is
785 // close to zero. We therefore don't try to overcome this
786 // problem with e.g. asking the user for a new path and
787 // trying again but simply exit.
791 if (lyxerr.debugging(Debug::INIT)) {
792 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
795 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
796 pimpl_->session_.reset(new lyx::Session(lyxrc.num_lastfiles));
801 void LyX::defaultKeyBindings(kb_keymap * kbmap)
803 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
804 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
805 kbmap->bind("Up", FuncRequest(LFUN_UP));
806 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
808 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
809 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
810 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
811 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
813 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
814 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
815 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
816 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
818 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
819 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
821 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
822 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
824 // kbmap->bindings to enable the use of the numeric keypad
826 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
827 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
828 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
829 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
830 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
831 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
832 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
833 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
834 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
835 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
836 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
837 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
838 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
839 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
840 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
841 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
842 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
843 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
844 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
845 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
846 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
847 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
848 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
849 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
853 void LyX::emergencyCleanup() const
855 // what to do about tmpfiles is non-obvious. we would
856 // like to delete any we find, but our lyxdir might
857 // contain documents etc. which might be helpful on
860 pimpl_->buffer_list_.emergencyWriteAll();
862 pimpl_->lyx_server_->emergencyCleanup();
863 pimpl_->lyx_server_.reset();
864 pimpl_->lyx_socket_.reset();
869 void LyX::deadKeyBindings(kb_keymap * kbmap)
871 // bindKeyings for transparent handling of deadkeys
872 // The keysyms are gotten from XFree86 X11R6
873 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
874 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
875 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
876 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
877 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
878 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
879 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
880 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
881 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
882 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
883 // nothing with this name
884 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
885 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
886 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
887 // nothing with this name either...
888 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
889 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
890 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
891 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
897 // return true if file does not exist or is older than configure.py.
898 bool needsUpdate(string const & file)
900 static string const configure_script =
901 addName(package().system_support(), "configure.py");
902 string const absfile =
903 addName(package().user_support(), file);
905 return (! fs::exists(absfile))
906 || (fs::last_write_time(configure_script)
907 > fs::last_write_time(absfile));
913 bool LyX::queryUserLyXDir(bool explicit_userdir)
915 // Does user directory exist?
916 if (fs::exists(package().user_support()) &&
917 fs::is_directory(package().user_support())) {
920 return needsUpdate("lyxrc.defaults")
921 || needsUpdate("textclass.lst")
922 || needsUpdate("packages.lst");
925 first_start = !explicit_userdir;
927 // If the user specified explicitly a directory, ask whether
928 // to create it. If the user says "no", then exit.
929 if (explicit_userdir &&
931 _("Missing user LyX directory"),
932 bformat(_("You have specified a non-existent user "
933 "LyX directory, %1$s.\n"
934 "It is needed to keep your own configuration."),
935 lyx::from_utf8(package().user_support())),
937 _("&Create directory"),
939 lyxerr << lyx::to_utf8(_("No user LyX directory. Exiting.")) << endl;
940 earlyExit(EXIT_FAILURE);
943 lyxerr << lyx::to_utf8(bformat(_("LyX: Creating directory %1$s"),
944 lyx::from_utf8(package().user_support())))
947 if (!createDirectory(package().user_support(), 0755)) {
948 // Failed, so let's exit.
949 lyxerr << lyx::to_utf8(_("Failed to create directory. Exiting."))
951 earlyExit(EXIT_FAILURE);
958 bool LyX::readRcFile(string const & name)
960 lyxerr[Debug::INIT] << "About to read " << name << "... ";
962 string const lyxrc_path = libFileSearch(string(), name);
963 if (!lyxrc_path.empty()) {
965 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
967 if (lyxrc.read(lyxrc_path) < 0) {
972 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
978 // Read the ui file `name'
979 bool LyX::readUIFile(string const & name)
989 struct keyword_item uitags[ui_last - 1] = {
990 { "include", ui_include },
991 { "menuset", ui_menuset },
992 { "toolbar", ui_toolbar },
993 { "toolbars", ui_toolbars }
996 // Ensure that a file is read only once (prevents include loops)
997 static std::list<string> uifiles;
998 std::list<string>::const_iterator it = uifiles.begin();
999 std::list<string>::const_iterator end = uifiles.end();
1000 it = std::find(it, end, name);
1002 lyxerr[Debug::INIT] << "UI file '" << name
1003 << "' has been read already. "
1004 << "Is this an include loop?"
1009 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1011 string const ui_path = libFileSearch("ui", name, "ui");
1013 if (ui_path.empty()) {
1014 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1015 showFileError(name);
1018 uifiles.push_back(name);
1020 lyxerr[Debug::INIT] << "Found " << name
1021 << " in " << ui_path << endl;
1022 LyXLex lex(uitags, ui_last - 1);
1023 lex.setFile(ui_path);
1025 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1029 if (lyxerr.debugging(Debug::PARSER))
1030 lex.printTable(lyxerr);
1032 while (lex.isOK()) {
1033 switch (lex.lex()) {
1036 string const file = lex.getString();
1037 if (!readUIFile(file))
1042 menubackend.read(lex);
1046 toolbarbackend.read(lex);
1050 toolbarbackend.readToolbars(lex);
1054 if (!rtrim(lex.getString()).empty())
1055 lex.printError("LyX::ReadUIFile: "
1056 "Unknown menu tag: `$$Token'");
1064 // Read the languages file `name'
1065 bool LyX::readLanguagesFile(string const & name)
1067 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1069 string const lang_path = libFileSearch(string(), name);
1070 if (lang_path.empty()) {
1071 showFileError(name);
1074 languages.read(lang_path);
1079 // Read the encodings file `name'
1080 bool LyX::readEncodingsFile(string const & name)
1082 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1084 string const enc_path = libFileSearch(string(), name);
1085 if (enc_path.empty()) {
1086 showFileError(name);
1089 encodings.read(enc_path);
1098 /// return the the number of arguments consumed
1099 typedef boost::function<int(string const &, string const &)> cmd_helper;
1101 int parse_dbg(string const & arg, string const &)
1104 lyxerr << lyx::to_utf8(_("List of supported debug flags:")) << endl;
1105 Debug::showTags(lyxerr);
1108 lyxerr << lyx::to_utf8(bformat(_("Setting debug level to %1$s"), lyx::from_utf8(arg))) << endl;
1110 lyxerr.level(Debug::value(arg));
1111 Debug::showLevel(lyxerr, lyxerr.level());
1116 int parse_help(string const &, string const &)
1119 lyx::to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1120 "Command line switches (case sensitive):\n"
1121 "\t-help summarize LyX usage\n"
1122 "\t-userdir dir set user directory to dir\n"
1123 "\t-sysdir dir set system directory to dir\n"
1124 "\t-geometry WxH+X+Y set geometry of the main window\n"
1125 "\t-dbg feature[,feature]...\n"
1126 " select the features to debug.\n"
1127 " Type `lyx -dbg' to see the list of features\n"
1128 "\t-x [--execute] command\n"
1129 " where command is a lyx command.\n"
1130 "\t-e [--export] fmt\n"
1131 " where fmt is the export format of choice.\n"
1132 "\t-i [--import] fmt file.xxx\n"
1133 " where fmt is the import format of choice\n"
1134 " and file.xxx is the file to be imported.\n"
1135 "\t-version summarize version and build info\n"
1136 "Check the LyX man page for more details.")) << endl;
1141 int parse_version(string const &, string const &)
1143 lyxerr << "LyX " << lyx_version
1144 << " (" << lyx_release_date << ")" << endl;
1145 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1147 lyxerr << lyx_version_info << endl;
1152 int parse_sysdir(string const & arg, string const &)
1155 lyxerr << lyx::to_utf8(_("Missing directory for -sysdir switch")) << endl;
1158 cl_system_support = arg;
1162 int parse_userdir(string const & arg, string const &)
1165 lyxerr << lyx::to_utf8(_("Missing directory for -userdir switch")) << endl;
1168 cl_user_support = arg;
1172 int parse_execute(string const & arg, string const &)
1175 lyxerr << lyx::to_utf8(_("Missing command string after --execute switch")) << endl;
1182 int parse_export(string const & type, string const &)
1185 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1186 "--export switch")) << endl;
1189 batch = "buffer-export " + type;
1190 lyx::use_gui = false;
1194 int parse_import(string const & type, string const & file)
1197 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1198 "--import switch")) << endl;
1202 lyxerr << lyx::to_utf8(_("Missing filename for --import")) << endl;
1206 batch = "buffer-import " + type + ' ' + file;
1213 void LyX::easyParse(int & argc, char * argv[])
1215 std::map<string, cmd_helper> cmdmap;
1217 cmdmap["-dbg"] = parse_dbg;
1218 cmdmap["-help"] = parse_help;
1219 cmdmap["--help"] = parse_help;
1220 cmdmap["-version"] = parse_version;
1221 cmdmap["--version"] = parse_version;
1222 cmdmap["-sysdir"] = parse_sysdir;
1223 cmdmap["-userdir"] = parse_userdir;
1224 cmdmap["-x"] = parse_execute;
1225 cmdmap["--execute"] = parse_execute;
1226 cmdmap["-e"] = parse_export;
1227 cmdmap["--export"] = parse_export;
1228 cmdmap["-i"] = parse_import;
1229 cmdmap["--import"] = parse_import;
1231 for (int i = 1; i < argc; ++i) {
1232 std::map<string, cmd_helper>::const_iterator it
1233 = cmdmap.find(argv[i]);
1235 // check for X11 -geometry option
1236 if (lyx::support::compare(argv[i], "-geometry") == 0)
1237 geometryOption_ = true;
1239 // don't complain if not found - may be parsed later
1240 if (it == cmdmap.end())
1243 string arg((i + 1 < argc) ? argv[i + 1] : "");
1244 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1246 int const remove = 1 + it->second(arg, arg2);
1248 // Now, remove used arguments by shifting
1249 // the following ones remove places down.
1251 for (int j = i; j < argc; ++j)
1252 argv[j] = argv[j + remove];
1256 batch_command = batch;
1261 FuncStatus getStatus(FuncRequest const & action)
1263 return LyX::ref().lyxFunc().getStatus(action);
1267 void dispatch(FuncRequest const & action)
1269 LyX::ref().lyxFunc().dispatch(action);
1275 BufferList & theBufferList()
1277 return LyX::ref().bufferList();
1281 LyXFunc & theLyXFunc()
1283 return LyX::ref().lyxFunc();
1287 LyXServer & theLyXServer()
1289 // FIXME: this should not be use_gui dependent
1290 BOOST_ASSERT(lyx::use_gui);
1291 return LyX::ref().server();
1295 LyXServerSocket & theLyXServerSocket()
1297 // FIXME: this should not be use_gui dependent
1298 BOOST_ASSERT(lyx::use_gui);
1299 return LyX::ref().socket();
1303 kb_keymap & theTopLevelKeymap()
1305 BOOST_ASSERT(lyx::use_gui);
1306 return LyX::ref().topLevelKeymap();