3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
20 #include "ConverterCache.h"
22 #include "buffer_funcs.h"
23 #include "bufferlist.h"
24 #include "converter.h"
27 #include "errorlist.h"
35 #include "LyXAction.h"
39 #include "lyxserver.h"
40 #include "lyxsocket.h"
41 #include "lyxtextclasslist.h"
42 #include "MenuBackend.h"
44 #include "ToolbarBackend.h"
46 #include "frontends/Alert.h"
47 #include "frontends/Application.h"
48 #include "frontends/Gui.h"
49 #include "frontends/LyXView.h"
51 #include "support/environment.h"
52 #include "support/filetools.h"
53 #include "support/fontutils.h"
54 #include "support/lyxlib.h"
55 #include "support/convert.h"
56 #include "support/os.h"
57 #include "support/package.h"
58 #include "support/path.h"
59 #include "support/systemcall.h"
60 #include "support/unicode.h"
62 #include <boost/bind.hpp>
63 #include <boost/filesystem/operations.hpp>
71 using support::addName;
72 using support::addPath;
73 using support::bformat;
74 using support::createDirectory;
75 using support::createLyXTmpDir;
76 using support::destroyDir;
77 using support::fileSearch;
78 using support::getEnv;
79 using support::i18nLibFileSearch;
80 using support::libFileSearch;
81 using support::package;
82 using support::prependEnvPath;
84 using support::Systemcall;
86 namespace Alert = frontend::Alert;
87 namespace os = support::os;
88 namespace fs = boost::filesystem;
95 #ifndef CXX_GLOBAL_CSTD
102 frontend::Application * theApp = 0;
104 /// are we using the GUI at all?
106 * We default to true and this is changed to false when the export feature is used.
113 // Filled with the command line arguments "foo" of "-sysdir foo" or
115 string cl_system_support;
116 string cl_user_support;
118 LyX * singleton_ = 0;
120 void showFileError(string const & error)
122 Alert::warning(_("Could not read configuration file"),
123 bformat(_("Error while reading the configuration file\n%1$s.\n"
124 "Please check your installation."), from_utf8(error)));
128 void reconfigureUserLyXDir()
130 string const configure_command = package().configure_command();
132 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
133 support::Path p(package().user_support());
135 one.startscript(Systemcall::Wait, configure_command);
136 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
142 /// The main application class private implementation.
143 struct LyX::Singletons
145 Singletons(): iconv(ucs4_codeset, "UTF-8")
148 /// our function handler
151 BufferList buffer_list_;
153 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
155 boost::scoped_ptr<LyXServer> lyx_server_;
157 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
159 boost::scoped_ptr<frontend::Application> application_;
160 /// lyx session, containing lastfiles, lastfilepos, and lastopened
161 boost::scoped_ptr<Session> session_;
164 IconvProcessor iconv;
170 // Static data are not treated in the same way at all on the Mac (and
171 // the LyX singleton has static methods). This is the reason why the
172 // exit command on the Mac bypasses our dispatch machinery altogether.
173 // On Linux and Windows we won't pass a second time through quit()
174 // because quitting will already be set to true.
182 BOOST_ASSERT(singleton_);
187 LyX const & LyX::cref()
189 BOOST_ASSERT(singleton_);
195 : first_start(false), geometryOption_(false)
198 pimpl_.reset(new Singletons);
202 BufferList & LyX::bufferList()
204 return pimpl_->buffer_list_;
208 BufferList const & LyX::bufferList() const
210 return pimpl_->buffer_list_;
214 Session & LyX::session()
216 BOOST_ASSERT(pimpl_->session_.get());
217 return *pimpl_->session_.get();
221 Session const & LyX::session() const
223 BOOST_ASSERT(pimpl_->session_.get());
224 return *pimpl_->session_.get();
228 LyXFunc & LyX::lyxFunc()
230 return pimpl_->lyxfunc_;
234 LyXFunc const & LyX::lyxFunc() const
236 return pimpl_->lyxfunc_;
240 LyXServer & LyX::server()
242 BOOST_ASSERT(pimpl_->lyx_server_.get());
243 return *pimpl_->lyx_server_.get();
247 LyXServer const & LyX::server() const
249 BOOST_ASSERT(pimpl_->lyx_server_.get());
250 return *pimpl_->lyx_server_.get();
254 LyXServerSocket & LyX::socket()
256 BOOST_ASSERT(pimpl_->lyx_socket_.get());
257 return *pimpl_->lyx_socket_.get();
261 LyXServerSocket const & LyX::socket() const
263 BOOST_ASSERT(pimpl_->lyx_socket_.get());
264 return *pimpl_->lyx_socket_.get();
268 frontend::Application & LyX::application()
270 BOOST_ASSERT(pimpl_->application_.get());
271 return *pimpl_->application_.get();
275 frontend::Application const & LyX::application() const
277 BOOST_ASSERT(pimpl_->application_.get());
278 return *pimpl_->application_.get();
282 kb_keymap & LyX::topLevelKeymap()
284 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
285 return *pimpl_->toplevel_keymap_.get();
289 IconvProcessor & LyX::iconvProcessor()
291 return pimpl_->iconv;
295 kb_keymap const & LyX::topLevelKeymap() const
297 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
298 return *pimpl_->toplevel_keymap_.get();
302 Buffer const * const LyX::updateInset(InsetBase const * inset) const
307 Buffer const * buffer_ptr = 0;
308 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
309 vector<int>::const_iterator it = view_ids.begin();
310 vector<int>::const_iterator const end = view_ids.end();
311 for (; it != end; ++it) {
313 pimpl_->application_->gui().view(*it).updateInset(inset);
321 int LyX::exec(int & argc, char * argv[])
323 // Here we need to parse the command line. At least
324 // we need to parse for "-dbg" and "-help"
325 easyParse(argc, argv);
327 support::init_package(argv[0], cl_system_support, cl_user_support,
328 support::top_build_dir_is_one_level_up);
330 vector<string> files;
331 int exit_status = execBatchCommands(argc, argv, files);
337 // Force adding of font path _before_ Application is initialized
338 support::addFontResources();
339 pimpl_->application_.reset(createApplication(argc, argv));
341 // FIXME: this global pointer should probably go.
342 theApp = pimpl_->application_.get();
343 restoreGuiSession(files);
344 // Start the real execution loop.
347 /* Create a CoreApplication class that will provide the main event loop
348 * and the socket callback registering. With Qt4, only QtCore
349 * library would be needed.
350 * When this is done, a server_mode could be created and the following two
351 * line would be moved out from here.
353 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
354 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
355 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
357 // handle the batch commands the user asked for
358 if (!batch_command.empty()) {
359 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
362 exit_status = pimpl_->application_->start(batch_command);
363 // Kill the application object before exiting. This avoid crash
365 pimpl_->application_.reset();
366 // Restore original font resources after Application is destroyed.
367 support::restoreFontResources();
370 // FIXME: create a ConsoleApplication
378 void LyX::prepareExit()
380 // Set a flag that we do quitting from the program,
381 // so no refreshes are necessary.
384 // close buffers first
385 pimpl_->buffer_list_.closeAll();
387 // do any other cleanup procedures now
388 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
390 if (!destroyDir(package().temp_dir())) {
391 docstring const msg =
392 bformat(_("Unable to remove the temporary directory %1$s"),
393 from_utf8(package().temp_dir()));
394 Alert::warning(_("Unable to remove temporary directory"), msg);
399 void LyX::earlyExit(int status)
401 BOOST_ASSERT(pimpl_->application_.get());
402 // LyX::pimpl_::application_ is not initialised at this
403 // point so it's safe to just exit after some cleanup.
411 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
416 pimpl_->session_->writeFile();
417 pimpl_->lyx_server_.reset();
418 pimpl_->lyx_socket_.reset();
419 pimpl_->application_->exit(0);
425 int LyX::execBatchCommands(int & argc, char * argv[],
426 vector<string> & files)
428 // Initialization of LyX (reads lyxrc and more)
429 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
430 bool success = init();
431 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
435 for (int argi = argc - 1; argi >= 1; --argi) {
436 // check for any remaining extra arguments other than
437 // document file names. These will be passed out to the
439 if (argv[argi][0] == '-')
441 files.push_back(os::internal_path(argv[argi]));
445 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
447 // Execute batch commands if available
448 if (!batch_command.empty()) {
450 lyxerr[Debug::INIT] << "About to handle -x '"
451 << batch_command << '\'' << endl;
453 Buffer * last_loaded = 0;
455 vector<string>::const_iterator it = files.begin();
456 vector<string>::const_iterator end = files.end();
458 for (; it != end; ++it) {
459 // get absolute path of file and add ".lyx" to
460 // the filename if necessary
461 string s = fileSearch(string(), *it, "lyx");
463 Buffer * const b = newFile(*it, string(), true);
467 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
468 if (loadLyXFile(buf, s)) {
470 ErrorList const & el = buf->errorList("Parse");
472 for_each(el.begin(), el.end(),
473 boost::bind(&LyX::printError, this, _1));
476 pimpl_->buffer_list_.release(buf);
480 // try to dispatch to last loaded buffer first
483 if (last_loaded->dispatch(batch_command, &success)) {
488 files.clear(); // the files are already loaded
495 void LyX::restoreGuiSession(vector<string> const & files)
497 LyXView * view = newLyXView();
500 for_each(files.begin(), files.end(),
501 bind(&LyXView::loadLyXFile, view, _1, true));
503 // if a file is specified, I assume that user wants to edit *that* file
504 if (files.empty() && lyxrc.load_session) {
505 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
506 // do not add to the lastfile list since these files are restored from
507 // last seesion, and should be already there (regular files), or should
508 // not be added at all (help files).
509 for_each(lastopened.begin(), lastopened.end(),
510 bind(&LyXView::loadLyXFile, view, _1, false));
512 // clear this list to save a few bytes of RAM
513 pimpl_->session_->lastOpened().clear();
517 LyXView * LyX::newLyXView()
522 // determine windows size and position, from lyxrc and/or session
524 unsigned int width = 690;
525 unsigned int height = 510;
526 bool maximize = false;
528 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
529 width = lyxrc.geometry_width;
530 height = lyxrc.geometry_height;
532 // if lyxrc returns (0,0), then use session info
534 string val = session().sessionInfo().load("WindowWidth");
536 width = convert<unsigned int>(val);
537 val = session().sessionInfo().load("WindowHeight");
539 height = convert<unsigned int>(val);
540 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
544 // if user wants to restore window position
547 if (lyxrc.geometry_xysaved) {
548 string val = session().sessionInfo().load("WindowPosX");
550 posx = convert<int>(val);
551 val = session().sessionInfo().load("WindowPosY");
553 posy = convert<int>(val);
556 if (geometryOption_) {
560 // create the main window
561 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
569 The SIGHUP signal does not exist on Windows and does not need to be handled.
571 Windows handles SIGFPE and SIGSEGV signals as expected.
573 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
574 cause a new thread to be spawned. This may well result in unexpected
575 behaviour by the single-threaded LyX.
577 SIGTERM signals will come only from another process actually sending
578 that signal using 'raise' in Windows' POSIX compatability layer. It will
579 not come from the general "terminate process" methods that everyone
580 actually uses (and which can't be trapped). Killing an app 'politely' on
581 Windows involves first sending a WM_CLOSE message, something that is
582 caught already by the Qt frontend.
584 For more information see:
586 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
587 ...signals are mostly useless on Windows for a variety of reasons that are
590 'UNIX Application Migration Guide, Chapter 9'
591 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
593 'How To Terminate an Application "Cleanly" in Win32'
594 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
598 static void error_handler(int err_sig)
600 // Throw away any signals other than the first one received.
601 static sig_atomic_t handling_error = false;
604 handling_error = true;
606 // We have received a signal indicating a fatal error, so
607 // try and save the data ASAP.
608 LyX::cref().emergencyCleanup();
610 // These lyxerr calls may or may not work:
612 // Signals are asynchronous, so the main program may be in a very
613 // fragile state when a signal is processed and thus while a signal
614 // handler function executes.
615 // In general, therefore, we should avoid performing any
616 // I/O operations or calling most library and system functions from
619 // This shouldn't matter here, however, as we've already invoked
624 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
628 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
631 lyxerr << "\nlyx: SIGSEGV signal caught\n"
632 "Sorry, you have found a bug in LyX. "
633 "Please read the bug-reporting instructions "
634 "in Help->Introduction and send us a bug report, "
635 "if necessary. Thanks !\nBye." << endl;
643 // Deinstall the signal handlers
645 signal(SIGHUP, SIG_DFL);
647 signal(SIGINT, SIG_DFL);
648 signal(SIGFPE, SIG_DFL);
649 signal(SIGSEGV, SIG_DFL);
650 signal(SIGTERM, SIG_DFL);
653 if (err_sig == SIGSEGV ||
654 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
656 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
665 void LyX::printError(ErrorItem const & ei)
667 docstring tmp = _("LyX: ") + ei.error + char_type(':')
669 std::cerr << to_utf8(tmp) << std::endl;
673 void LyX::initGuiFont()
675 if (lyxrc.roman_font_name.empty())
676 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
678 if (lyxrc.sans_font_name.empty())
679 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
681 if (lyxrc.typewriter_font_name.empty())
682 lyxrc.typewriter_font_name
683 = pimpl_->application_->typewriterFontName();
690 signal(SIGHUP, error_handler);
692 signal(SIGFPE, error_handler);
693 signal(SIGSEGV, error_handler);
694 signal(SIGINT, error_handler);
695 signal(SIGTERM, error_handler);
696 // SIGPIPE can be safely ignored.
698 lyxrc.tempdir_path = package().temp_dir();
699 lyxrc.document_path = package().document_dir();
701 if (lyxrc.template_path.empty()) {
702 lyxrc.template_path = addPath(package().system_support(),
707 // Read configuration files
710 // This one may have been distributed along with LyX.
711 if (!readRcFile("lyxrc.dist"))
714 // Set the PATH correctly.
715 #if !defined (USE_POSIX_PACKAGING)
716 // Add the directory containing the LyX executable to the path
717 // so that LyX can find things like tex2lyx.
718 if (package().build_support().empty())
719 prependEnvPath("PATH", package().binary_dir());
721 if (!lyxrc.path_prefix.empty())
722 prependEnvPath("PATH", lyxrc.path_prefix);
724 // Check that user LyX directory is ok.
725 if (queryUserLyXDir(package().explicit_user_support()))
726 reconfigureUserLyXDir();
728 // no need for a splash when there is no GUI
733 // This one is generated in user_support directory by lib/configure.py.
734 if (!readRcFile("lyxrc.defaults"))
737 // Query the OS to know what formats are viewed natively
738 formats.setAutoOpen();
740 // Read lyxrc.dist again to be able to override viewer auto-detection.
741 readRcFile("lyxrc.dist");
743 system_lyxrc = lyxrc;
744 system_formats = formats;
745 system_converters = converters;
746 system_movers = movers;
747 system_lcolor = lcolor;
749 // This one is edited through the preferences dialog.
750 if (!readRcFile("preferences"))
753 if (!readEncodingsFile("encodings"))
755 if (!readLanguagesFile("languages"))
759 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
765 pimpl_->toplevel_keymap_.reset(new kb_keymap);
766 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
767 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
769 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
772 if (!readUIFile(lyxrc.ui_file))
776 if (lyxerr.debugging(Debug::LYXRC))
779 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
780 if (!lyxrc.path_prefix.empty())
781 prependEnvPath("PATH", lyxrc.path_prefix);
783 if (fs::exists(lyxrc.document_path) &&
784 fs::is_directory(lyxrc.document_path))
785 package().document_dir() = lyxrc.document_path;
787 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
788 if (package().temp_dir().empty()) {
789 Alert::error(_("Could not create temporary directory"),
790 bformat(_("Could not create a temporary directory in\n"
791 "%1$s. Make sure that this\n"
792 "path exists and is writable and try again."),
793 from_utf8(lyxrc.tempdir_path)));
794 // createLyXTmpDir() tries sufficiently hard to create a
795 // usable temp dir, so the probability to come here is
796 // close to zero. We therefore don't try to overcome this
797 // problem with e.g. asking the user for a new path and
798 // trying again but simply exit.
802 if (lyxerr.debugging(Debug::INIT)) {
803 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
806 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
807 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
809 // This must happen after package initialization and after lyxrc is
810 // read, therefore it can't be done by a static object.
811 ConverterCache::init();
817 void LyX::defaultKeyBindings(kb_keymap * kbmap)
819 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
820 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
821 kbmap->bind("Up", FuncRequest(LFUN_UP));
822 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
824 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
825 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
826 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
827 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
829 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
830 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
831 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
832 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
834 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
835 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
837 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
838 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
840 // kbmap->bindings to enable the use of the numeric keypad
842 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
843 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
844 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
845 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
846 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
847 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
848 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
849 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
850 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
851 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
852 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
853 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
854 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
855 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
856 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
857 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
858 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
859 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
860 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
861 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
862 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
863 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
864 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
865 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
869 void LyX::emergencyCleanup() const
871 // what to do about tmpfiles is non-obvious. we would
872 // like to delete any we find, but our lyxdir might
873 // contain documents etc. which might be helpful on
876 pimpl_->buffer_list_.emergencyWriteAll();
878 pimpl_->lyx_server_->emergencyCleanup();
879 pimpl_->lyx_server_.reset();
880 pimpl_->lyx_socket_.reset();
885 void LyX::deadKeyBindings(kb_keymap * kbmap)
887 // bindKeyings for transparent handling of deadkeys
888 // The keysyms are gotten from XFree86 X11R6
889 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
890 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
891 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
892 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
893 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
894 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
895 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
896 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
897 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
898 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
899 // nothing with this name
900 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
901 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
902 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
903 // nothing with this name either...
904 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
905 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
906 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
907 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
913 // return true if file does not exist or is older than configure.py.
914 bool needsUpdate(string const & file)
916 static string const configure_script =
917 addName(package().system_support(), "configure.py");
918 string const absfile =
919 addName(package().user_support(), file);
921 return (! fs::exists(absfile))
922 || (fs::last_write_time(configure_script)
923 > fs::last_write_time(absfile));
929 bool LyX::queryUserLyXDir(bool explicit_userdir)
931 // Does user directory exist?
932 if (fs::exists(package().user_support()) &&
933 fs::is_directory(package().user_support())) {
936 return needsUpdate("lyxrc.defaults")
937 || needsUpdate("textclass.lst")
938 || needsUpdate("packages.lst");
941 first_start = !explicit_userdir;
943 // If the user specified explicitly a directory, ask whether
944 // to create it. If the user says "no", then exit.
945 if (explicit_userdir &&
947 _("Missing user LyX directory"),
948 bformat(_("You have specified a non-existent user "
949 "LyX directory, %1$s.\n"
950 "It is needed to keep your own configuration."),
951 from_utf8(package().user_support())),
953 _("&Create directory"),
955 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
956 earlyExit(EXIT_FAILURE);
959 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
960 from_utf8(package().user_support())))
963 if (!createDirectory(package().user_support(), 0755)) {
964 // Failed, so let's exit.
965 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
967 earlyExit(EXIT_FAILURE);
974 bool LyX::readRcFile(string const & name)
976 lyxerr[Debug::INIT] << "About to read " << name << "... ";
978 string const lyxrc_path = libFileSearch(string(), name);
979 if (!lyxrc_path.empty()) {
981 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
983 if (lyxrc.read(lyxrc_path) < 0) {
988 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
994 // Read the ui file `name'
995 bool LyX::readUIFile(string const & name)
1005 struct keyword_item uitags[ui_last - 1] = {
1006 { "include", ui_include },
1007 { "menuset", ui_menuset },
1008 { "toolbar", ui_toolbar },
1009 { "toolbars", ui_toolbars }
1012 // Ensure that a file is read only once (prevents include loops)
1013 static std::list<string> uifiles;
1014 std::list<string>::const_iterator it = uifiles.begin();
1015 std::list<string>::const_iterator end = uifiles.end();
1016 it = std::find(it, end, name);
1018 lyxerr[Debug::INIT] << "UI file '" << name
1019 << "' has been read already. "
1020 << "Is this an include loop?"
1025 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1027 string const ui_path = libFileSearch("ui", name, "ui");
1029 if (ui_path.empty()) {
1030 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1031 showFileError(name);
1034 uifiles.push_back(name);
1036 lyxerr[Debug::INIT] << "Found " << name
1037 << " in " << ui_path << endl;
1038 LyXLex lex(uitags, ui_last - 1);
1039 lex.setFile(ui_path);
1041 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1045 if (lyxerr.debugging(Debug::PARSER))
1046 lex.printTable(lyxerr);
1048 while (lex.isOK()) {
1049 switch (lex.lex()) {
1052 string const file = lex.getString();
1053 if (!readUIFile(file))
1058 menubackend.read(lex);
1062 toolbarbackend.read(lex);
1066 toolbarbackend.readToolbars(lex);
1070 if (!rtrim(lex.getString()).empty())
1071 lex.printError("LyX::ReadUIFile: "
1072 "Unknown menu tag: `$$Token'");
1080 // Read the languages file `name'
1081 bool LyX::readLanguagesFile(string const & name)
1083 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1085 string const lang_path = libFileSearch(string(), name);
1086 if (lang_path.empty()) {
1087 showFileError(name);
1090 languages.read(lang_path);
1095 // Read the encodings file `name'
1096 bool LyX::readEncodingsFile(string const & name)
1098 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1100 string const enc_path = libFileSearch(string(), name);
1101 if (enc_path.empty()) {
1102 showFileError(name);
1105 encodings.read(enc_path);
1114 /// return the the number of arguments consumed
1115 typedef boost::function<int(string const &, string const &)> cmd_helper;
1117 int parse_dbg(string const & arg, string const &)
1120 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1121 Debug::showTags(lyxerr);
1124 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1126 lyxerr.level(Debug::value(arg));
1127 Debug::showLevel(lyxerr, lyxerr.level());
1132 int parse_help(string const &, string const &)
1135 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1136 "Command line switches (case sensitive):\n"
1137 "\t-help summarize LyX usage\n"
1138 "\t-userdir dir set user directory to dir\n"
1139 "\t-sysdir dir set system directory to dir\n"
1140 "\t-geometry WxH+X+Y set geometry of the main window\n"
1141 "\t-dbg feature[,feature]...\n"
1142 " select the features to debug.\n"
1143 " Type `lyx -dbg' to see the list of features\n"
1144 "\t-x [--execute] command\n"
1145 " where command is a lyx command.\n"
1146 "\t-e [--export] fmt\n"
1147 " where fmt is the export format of choice.\n"
1148 "\t-i [--import] fmt file.xxx\n"
1149 " where fmt is the import format of choice\n"
1150 " and file.xxx is the file to be imported.\n"
1151 "\t-version summarize version and build info\n"
1152 "Check the LyX man page for more details.")) << endl;
1157 int parse_version(string const &, string const &)
1159 lyxerr << "LyX " << lyx_version
1160 << " (" << lyx_release_date << ")" << endl;
1161 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1163 lyxerr << lyx_version_info << endl;
1168 int parse_sysdir(string const & arg, string const &)
1171 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1174 cl_system_support = arg;
1178 int parse_userdir(string const & arg, string const &)
1181 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1184 cl_user_support = arg;
1188 int parse_execute(string const & arg, string const &)
1191 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1198 int parse_export(string const & type, string const &)
1201 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1202 "--export switch")) << endl;
1205 batch = "buffer-export " + type;
1210 int parse_import(string const & type, string const & file)
1213 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1214 "--import switch")) << endl;
1218 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1222 batch = "buffer-import " + type + ' ' + file;
1229 void LyX::easyParse(int & argc, char * argv[])
1231 std::map<string, cmd_helper> cmdmap;
1233 cmdmap["-dbg"] = parse_dbg;
1234 cmdmap["-help"] = parse_help;
1235 cmdmap["--help"] = parse_help;
1236 cmdmap["-version"] = parse_version;
1237 cmdmap["--version"] = parse_version;
1238 cmdmap["-sysdir"] = parse_sysdir;
1239 cmdmap["-userdir"] = parse_userdir;
1240 cmdmap["-x"] = parse_execute;
1241 cmdmap["--execute"] = parse_execute;
1242 cmdmap["-e"] = parse_export;
1243 cmdmap["--export"] = parse_export;
1244 cmdmap["-i"] = parse_import;
1245 cmdmap["--import"] = parse_import;
1247 for (int i = 1; i < argc; ++i) {
1248 std::map<string, cmd_helper>::const_iterator it
1249 = cmdmap.find(argv[i]);
1251 // check for X11 -geometry option
1252 if (support::compare(argv[i], "-geometry") == 0)
1253 geometryOption_ = true;
1255 // don't complain if not found - may be parsed later
1256 if (it == cmdmap.end())
1259 string arg((i + 1 < argc) ? argv[i + 1] : "");
1260 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1262 int const remove = 1 + it->second(arg, arg2);
1264 // Now, remove used arguments by shifting
1265 // the following ones remove places down.
1267 for (int j = i; j < argc; ++j)
1268 argv[j] = argv[j + remove];
1272 batch_command = batch;
1276 FuncStatus getStatus(FuncRequest const & action)
1278 return LyX::ref().lyxFunc().getStatus(action);
1282 void dispatch(FuncRequest const & action)
1284 LyX::ref().lyxFunc().dispatch(action);
1288 BufferList & theBufferList()
1290 return LyX::ref().bufferList();
1294 LyXFunc & theLyXFunc()
1296 return LyX::ref().lyxFunc();
1300 LyXServer & theLyXServer()
1302 // FIXME: this should not be use_gui dependent
1303 BOOST_ASSERT(use_gui);
1304 return LyX::ref().server();
1308 LyXServerSocket & theLyXServerSocket()
1310 // FIXME: this should not be use_gui dependent
1311 BOOST_ASSERT(use_gui);
1312 return LyX::ref().socket();
1316 kb_keymap & theTopLevelKeymap()
1318 BOOST_ASSERT(use_gui);
1319 return LyX::ref().topLevelKeymap();
1323 IconvProcessor & utf8ToUcs4()
1325 return LyX::ref().iconvProcessor();