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;
333 // FIXME: create a ConsoleApplication
334 int exit_status = loadFiles(argc, argv, files);
339 Buffer * last_loaded = pimpl_->buffer_list_.last();
340 if (batch_command.empty() || !last_loaded) {
345 // try to dispatch to last loaded buffer first
346 bool success = false;
347 last_loaded->dispatch(batch_command, &success);
352 // Force adding of font path _before_ Application is initialized
353 support::addFontResources();
355 // Let the frontend parse and remove all arguments that it knows
356 pimpl_->application_.reset(createApplication(argc, argv));
357 // FIXME: this global pointer should probably go.
358 theApp = pimpl_->application_.get();
362 // Parse and remove all known arguments in the LyX singleton
363 // Give an error for all remaining ones.
364 int exit_status = loadFiles(argc, argv, files);
366 // Kill the application object before exiting.
367 pimpl_->application_.reset();
373 restoreGuiSession(files);
374 // Start the real execution loop.
377 /* Create a CoreApplication class that will provide the main event loop
378 * and the socket callback registering. With Qt4, only QtCore
379 * library would be needed.
380 * When this is done, a server_mode could be created and the following two
381 * line would be moved out from here.
383 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
384 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
385 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
387 exit_status = pimpl_->application_->exec();
388 // Kill the application object before exiting. This avoid crash
390 pimpl_->application_.reset();
391 // Restore original font resources after Application is destroyed.
392 support::restoreFontResources();
398 void LyX::prepareExit()
400 // Set a flag that we do quitting from the program,
401 // so no refreshes are necessary.
404 // close buffers first
405 pimpl_->buffer_list_.closeAll();
407 // do any other cleanup procedures now
408 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
410 if (!destroyDir(package().temp_dir())) {
411 docstring const msg =
412 bformat(_("Unable to remove the temporary directory %1$s"),
413 from_utf8(package().temp_dir()));
414 Alert::warning(_("Unable to remove temporary directory"), msg);
419 void LyX::earlyExit(int status)
421 BOOST_ASSERT(pimpl_->application_.get());
422 // LyX::pimpl_::application_ is not initialised at this
423 // point so it's safe to just exit after some cleanup.
431 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
436 pimpl_->session_->writeFile();
437 pimpl_->lyx_server_.reset();
438 pimpl_->lyx_socket_.reset();
439 pimpl_->application_->exit(0);
445 int LyX::loadFiles(int & argc, char * argv[],
446 vector<string> & files)
448 // check for any spurious extra arguments
449 // other than documents
450 for (int argi = 1; argi < argc ; ++argi) {
451 if (argv[argi][0] == '-') {
453 bformat(_("Wrong command line option `%1$s'. Exiting."),
454 from_utf8(argv[argi]))) << endl;
459 // Initialization of LyX (reads lyxrc and more)
460 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
461 bool success = init();
462 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
466 for (int argi = argc - 1; argi >= 1; --argi) {
467 // check for any remaining extra arguments other than
468 // document file names. These will be passed out to the
470 if (argv[argi][0] == '-')
472 files.push_back(os::internal_path(argv[argi]));
476 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
478 Buffer * last_loaded = 0;
480 vector<string>::const_iterator it = files.begin();
481 vector<string>::const_iterator end = files.end();
483 for (; it != end; ++it) {
484 // get absolute path of file and add ".lyx" to
485 // the filename if necessary
486 string s = fileSearch(string(), *it, "lyx");
488 Buffer * const b = newFile(*it, string(), true);
492 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
493 if (loadLyXFile(buf, s)) {
495 ErrorList const & el = buf->errorList("Parse");
497 for_each(el.begin(), el.end(),
498 boost::bind(&LyX::printError, this, _1));
501 pimpl_->buffer_list_.release(buf);
505 files.clear(); // the files are already loaded
511 void LyX::execBatchCommands()
513 // Execute batch commands if available
514 if (batch_command.empty())
517 lyxerr[Debug::INIT] << "About to handle -x '"
518 << batch_command << '\'' << endl;
520 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
524 void LyX::restoreGuiSession(vector<string> const & files)
526 LyXView * view = newLyXView();
529 for_each(files.begin(), files.end(),
530 bind(&LyXView::loadLyXFile, view, _1, true));
532 // if a file is specified, I assume that user wants to edit *that* file
533 if (files.empty() && lyxrc.load_session) {
534 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
535 // do not add to the lastfile list since these files are restored from
536 // last seesion, and should be already there (regular files), or should
537 // not be added at all (help files).
538 for_each(lastopened.begin(), lastopened.end(),
539 bind(&LyXView::loadLyXFile, view, _1, false));
541 // clear this list to save a few bytes of RAM
542 pimpl_->session_->lastOpened().clear();
546 LyXView * LyX::newLyXView()
551 // determine windows size and position, from lyxrc and/or session
553 unsigned int width = 690;
554 unsigned int height = 510;
555 bool maximize = false;
557 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
558 width = lyxrc.geometry_width;
559 height = lyxrc.geometry_height;
561 // if lyxrc returns (0,0), then use session info
563 string val = session().sessionInfo().load("WindowWidth");
565 width = convert<unsigned int>(val);
566 val = session().sessionInfo().load("WindowHeight");
568 height = convert<unsigned int>(val);
569 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
573 // if user wants to restore window position
576 if (lyxrc.geometry_xysaved) {
577 string val = session().sessionInfo().load("WindowPosX");
579 posx = convert<int>(val);
580 val = session().sessionInfo().load("WindowPosY");
582 posy = convert<int>(val);
585 if (geometryOption_) {
589 // create the main window
590 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
598 The SIGHUP signal does not exist on Windows and does not need to be handled.
600 Windows handles SIGFPE and SIGSEGV signals as expected.
602 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
603 cause a new thread to be spawned. This may well result in unexpected
604 behaviour by the single-threaded LyX.
606 SIGTERM signals will come only from another process actually sending
607 that signal using 'raise' in Windows' POSIX compatability layer. It will
608 not come from the general "terminate process" methods that everyone
609 actually uses (and which can't be trapped). Killing an app 'politely' on
610 Windows involves first sending a WM_CLOSE message, something that is
611 caught already by the Qt frontend.
613 For more information see:
615 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
616 ...signals are mostly useless on Windows for a variety of reasons that are
619 'UNIX Application Migration Guide, Chapter 9'
620 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
622 'How To Terminate an Application "Cleanly" in Win32'
623 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
627 static void error_handler(int err_sig)
629 // Throw away any signals other than the first one received.
630 static sig_atomic_t handling_error = false;
633 handling_error = true;
635 // We have received a signal indicating a fatal error, so
636 // try and save the data ASAP.
637 LyX::cref().emergencyCleanup();
639 // These lyxerr calls may or may not work:
641 // Signals are asynchronous, so the main program may be in a very
642 // fragile state when a signal is processed and thus while a signal
643 // handler function executes.
644 // In general, therefore, we should avoid performing any
645 // I/O operations or calling most library and system functions from
648 // This shouldn't matter here, however, as we've already invoked
653 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
657 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
660 lyxerr << "\nlyx: SIGSEGV signal caught\n"
661 "Sorry, you have found a bug in LyX. "
662 "Please read the bug-reporting instructions "
663 "in Help->Introduction and send us a bug report, "
664 "if necessary. Thanks !\nBye." << endl;
672 // Deinstall the signal handlers
674 signal(SIGHUP, SIG_DFL);
676 signal(SIGINT, SIG_DFL);
677 signal(SIGFPE, SIG_DFL);
678 signal(SIGSEGV, SIG_DFL);
679 signal(SIGTERM, SIG_DFL);
682 if (err_sig == SIGSEGV ||
683 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
685 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
694 void LyX::printError(ErrorItem const & ei)
696 docstring tmp = _("LyX: ") + ei.error + char_type(':')
698 std::cerr << to_utf8(tmp) << std::endl;
702 void LyX::initGuiFont()
704 if (lyxrc.roman_font_name.empty())
705 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
707 if (lyxrc.sans_font_name.empty())
708 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
710 if (lyxrc.typewriter_font_name.empty())
711 lyxrc.typewriter_font_name
712 = pimpl_->application_->typewriterFontName();
719 signal(SIGHUP, error_handler);
721 signal(SIGFPE, error_handler);
722 signal(SIGSEGV, error_handler);
723 signal(SIGINT, error_handler);
724 signal(SIGTERM, error_handler);
725 // SIGPIPE can be safely ignored.
727 lyxrc.tempdir_path = package().temp_dir();
728 lyxrc.document_path = package().document_dir();
730 if (lyxrc.template_path.empty()) {
731 lyxrc.template_path = addPath(package().system_support(),
736 // Read configuration files
739 // This one may have been distributed along with LyX.
740 if (!readRcFile("lyxrc.dist"))
743 // Set the PATH correctly.
744 #if !defined (USE_POSIX_PACKAGING)
745 // Add the directory containing the LyX executable to the path
746 // so that LyX can find things like tex2lyx.
747 if (package().build_support().empty())
748 prependEnvPath("PATH", package().binary_dir());
750 if (!lyxrc.path_prefix.empty())
751 prependEnvPath("PATH", lyxrc.path_prefix);
753 // Check that user LyX directory is ok.
754 if (queryUserLyXDir(package().explicit_user_support()))
755 reconfigureUserLyXDir();
757 // no need for a splash when there is no GUI
762 // This one is generated in user_support directory by lib/configure.py.
763 if (!readRcFile("lyxrc.defaults"))
766 // Query the OS to know what formats are viewed natively
767 formats.setAutoOpen();
769 // Read lyxrc.dist again to be able to override viewer auto-detection.
770 readRcFile("lyxrc.dist");
772 system_lyxrc = lyxrc;
773 system_formats = formats;
774 system_converters = converters;
775 system_movers = movers;
776 system_lcolor = lcolor;
778 // This one is edited through the preferences dialog.
779 if (!readRcFile("preferences"))
782 if (!readEncodingsFile("encodings"))
784 if (!readLanguagesFile("languages"))
788 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
794 pimpl_->toplevel_keymap_.reset(new kb_keymap);
795 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
796 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
798 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
801 if (!readUIFile(lyxrc.ui_file))
805 if (lyxerr.debugging(Debug::LYXRC))
808 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
809 if (!lyxrc.path_prefix.empty())
810 prependEnvPath("PATH", lyxrc.path_prefix);
812 if (fs::exists(lyxrc.document_path) &&
813 fs::is_directory(lyxrc.document_path))
814 package().document_dir() = lyxrc.document_path;
816 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
817 if (package().temp_dir().empty()) {
818 Alert::error(_("Could not create temporary directory"),
819 bformat(_("Could not create a temporary directory in\n"
820 "%1$s. Make sure that this\n"
821 "path exists and is writable and try again."),
822 from_utf8(lyxrc.tempdir_path)));
823 // createLyXTmpDir() tries sufficiently hard to create a
824 // usable temp dir, so the probability to come here is
825 // close to zero. We therefore don't try to overcome this
826 // problem with e.g. asking the user for a new path and
827 // trying again but simply exit.
831 if (lyxerr.debugging(Debug::INIT)) {
832 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
835 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
836 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
838 // This must happen after package initialization and after lyxrc is
839 // read, therefore it can't be done by a static object.
840 ConverterCache::init();
846 void LyX::defaultKeyBindings(kb_keymap * kbmap)
848 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
849 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
850 kbmap->bind("Up", FuncRequest(LFUN_UP));
851 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
853 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
854 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
855 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
856 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
858 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
859 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
860 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
861 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
863 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
864 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
866 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
867 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
869 // kbmap->bindings to enable the use of the numeric keypad
871 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
872 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
873 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
874 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
875 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
876 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
877 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
878 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
879 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
880 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
881 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
882 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
883 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
884 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
885 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
886 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
887 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
888 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
889 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
890 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
891 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
892 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
893 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
894 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
898 void LyX::emergencyCleanup() const
900 // what to do about tmpfiles is non-obvious. we would
901 // like to delete any we find, but our lyxdir might
902 // contain documents etc. which might be helpful on
905 pimpl_->buffer_list_.emergencyWriteAll();
907 pimpl_->lyx_server_->emergencyCleanup();
908 pimpl_->lyx_server_.reset();
909 pimpl_->lyx_socket_.reset();
914 void LyX::deadKeyBindings(kb_keymap * kbmap)
916 // bindKeyings for transparent handling of deadkeys
917 // The keysyms are gotten from XFree86 X11R6
918 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
919 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
920 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
921 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
922 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
923 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
924 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
925 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
926 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
927 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
928 // nothing with this name
929 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
930 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
931 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
932 // nothing with this name either...
933 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
934 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
935 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
936 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
942 // return true if file does not exist or is older than configure.py.
943 bool needsUpdate(string const & file)
945 static string const configure_script =
946 addName(package().system_support(), "configure.py");
947 string const absfile =
948 addName(package().user_support(), file);
950 return (! fs::exists(absfile))
951 || (fs::last_write_time(configure_script)
952 > fs::last_write_time(absfile));
958 bool LyX::queryUserLyXDir(bool explicit_userdir)
960 // Does user directory exist?
961 if (fs::exists(package().user_support()) &&
962 fs::is_directory(package().user_support())) {
965 return needsUpdate("lyxrc.defaults")
966 || needsUpdate("textclass.lst")
967 || needsUpdate("packages.lst");
970 first_start = !explicit_userdir;
972 // If the user specified explicitly a directory, ask whether
973 // to create it. If the user says "no", then exit.
974 if (explicit_userdir &&
976 _("Missing user LyX directory"),
977 bformat(_("You have specified a non-existent user "
978 "LyX directory, %1$s.\n"
979 "It is needed to keep your own configuration."),
980 from_utf8(package().user_support())),
982 _("&Create directory"),
984 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
985 earlyExit(EXIT_FAILURE);
988 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
989 from_utf8(package().user_support())))
992 if (!createDirectory(package().user_support(), 0755)) {
993 // Failed, so let's exit.
994 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
996 earlyExit(EXIT_FAILURE);
1003 bool LyX::readRcFile(string const & name)
1005 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1007 string const lyxrc_path = libFileSearch(string(), name);
1008 if (!lyxrc_path.empty()) {
1010 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1012 if (lyxrc.read(lyxrc_path) < 0) {
1013 showFileError(name);
1017 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1023 // Read the ui file `name'
1024 bool LyX::readUIFile(string const & name)
1034 struct keyword_item uitags[ui_last - 1] = {
1035 { "include", ui_include },
1036 { "menuset", ui_menuset },
1037 { "toolbar", ui_toolbar },
1038 { "toolbars", ui_toolbars }
1041 // Ensure that a file is read only once (prevents include loops)
1042 static std::list<string> uifiles;
1043 std::list<string>::const_iterator it = uifiles.begin();
1044 std::list<string>::const_iterator end = uifiles.end();
1045 it = std::find(it, end, name);
1047 lyxerr[Debug::INIT] << "UI file '" << name
1048 << "' has been read already. "
1049 << "Is this an include loop?"
1054 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1056 string const ui_path = libFileSearch("ui", name, "ui");
1058 if (ui_path.empty()) {
1059 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1060 showFileError(name);
1063 uifiles.push_back(name);
1065 lyxerr[Debug::INIT] << "Found " << name
1066 << " in " << ui_path << endl;
1067 LyXLex lex(uitags, ui_last - 1);
1068 lex.setFile(ui_path);
1070 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1074 if (lyxerr.debugging(Debug::PARSER))
1075 lex.printTable(lyxerr);
1077 while (lex.isOK()) {
1078 switch (lex.lex()) {
1081 string const file = lex.getString();
1082 if (!readUIFile(file))
1087 menubackend.read(lex);
1091 toolbarbackend.read(lex);
1095 toolbarbackend.readToolbars(lex);
1099 if (!rtrim(lex.getString()).empty())
1100 lex.printError("LyX::ReadUIFile: "
1101 "Unknown menu tag: `$$Token'");
1109 // Read the languages file `name'
1110 bool LyX::readLanguagesFile(string const & name)
1112 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1114 string const lang_path = libFileSearch(string(), name);
1115 if (lang_path.empty()) {
1116 showFileError(name);
1119 languages.read(lang_path);
1124 // Read the encodings file `name'
1125 bool LyX::readEncodingsFile(string const & name)
1127 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1129 string const enc_path = libFileSearch(string(), name);
1130 if (enc_path.empty()) {
1131 showFileError(name);
1134 encodings.read(enc_path);
1143 /// return the the number of arguments consumed
1144 typedef boost::function<int(string const &, string const &)> cmd_helper;
1146 int parse_dbg(string const & arg, string const &)
1149 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1150 Debug::showTags(lyxerr);
1153 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1155 lyxerr.level(Debug::value(arg));
1156 Debug::showLevel(lyxerr, lyxerr.level());
1161 int parse_help(string const &, string const &)
1164 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1165 "Command line switches (case sensitive):\n"
1166 "\t-help summarize LyX usage\n"
1167 "\t-userdir dir set user directory to dir\n"
1168 "\t-sysdir dir set system directory to dir\n"
1169 "\t-geometry WxH+X+Y set geometry of the main window\n"
1170 "\t-dbg feature[,feature]...\n"
1171 " select the features to debug.\n"
1172 " Type `lyx -dbg' to see the list of features\n"
1173 "\t-x [--execute] command\n"
1174 " where command is a lyx command.\n"
1175 "\t-e [--export] fmt\n"
1176 " where fmt is the export format of choice.\n"
1177 "\t-i [--import] fmt file.xxx\n"
1178 " where fmt is the import format of choice\n"
1179 " and file.xxx is the file to be imported.\n"
1180 "\t-version summarize version and build info\n"
1181 "Check the LyX man page for more details.")) << endl;
1186 int parse_version(string const &, string const &)
1188 lyxerr << "LyX " << lyx_version
1189 << " (" << lyx_release_date << ")" << endl;
1190 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1192 lyxerr << lyx_version_info << endl;
1197 int parse_sysdir(string const & arg, string const &)
1200 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1203 cl_system_support = arg;
1207 int parse_userdir(string const & arg, string const &)
1210 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1213 cl_user_support = arg;
1217 int parse_execute(string const & arg, string const &)
1220 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1227 int parse_export(string const & type, string const &)
1230 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1231 "--export switch")) << endl;
1234 batch = "buffer-export " + type;
1239 int parse_import(string const & type, string const & file)
1242 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1243 "--import switch")) << endl;
1247 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1251 batch = "buffer-import " + type + ' ' + file;
1258 void LyX::easyParse(int & argc, char * argv[])
1260 std::map<string, cmd_helper> cmdmap;
1262 cmdmap["-dbg"] = parse_dbg;
1263 cmdmap["-help"] = parse_help;
1264 cmdmap["--help"] = parse_help;
1265 cmdmap["-version"] = parse_version;
1266 cmdmap["--version"] = parse_version;
1267 cmdmap["-sysdir"] = parse_sysdir;
1268 cmdmap["-userdir"] = parse_userdir;
1269 cmdmap["-x"] = parse_execute;
1270 cmdmap["--execute"] = parse_execute;
1271 cmdmap["-e"] = parse_export;
1272 cmdmap["--export"] = parse_export;
1273 cmdmap["-i"] = parse_import;
1274 cmdmap["--import"] = parse_import;
1276 for (int i = 1; i < argc; ++i) {
1277 std::map<string, cmd_helper>::const_iterator it
1278 = cmdmap.find(argv[i]);
1280 // check for X11 -geometry option
1281 if (support::compare(argv[i], "-geometry") == 0)
1282 geometryOption_ = true;
1284 // don't complain if not found - may be parsed later
1285 if (it == cmdmap.end())
1288 string arg((i + 1 < argc) ? argv[i + 1] : "");
1289 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1291 int const remove = 1 + it->second(arg, arg2);
1293 // Now, remove used arguments by shifting
1294 // the following ones remove places down.
1296 for (int j = i; j < argc; ++j)
1297 argv[j] = argv[j + remove];
1301 batch_command = batch;
1305 FuncStatus getStatus(FuncRequest const & action)
1307 return LyX::ref().lyxFunc().getStatus(action);
1311 void dispatch(FuncRequest const & action)
1313 LyX::ref().lyxFunc().dispatch(action);
1317 BufferList & theBufferList()
1319 return LyX::ref().bufferList();
1323 LyXFunc & theLyXFunc()
1325 return LyX::ref().lyxFunc();
1329 LyXServer & theLyXServer()
1331 // FIXME: this should not be use_gui dependent
1332 BOOST_ASSERT(use_gui);
1333 return LyX::ref().server();
1337 LyXServerSocket & theLyXServerSocket()
1339 // FIXME: this should not be use_gui dependent
1340 BOOST_ASSERT(use_gui);
1341 return LyX::ref().socket();
1345 kb_keymap & theTopLevelKeymap()
1347 BOOST_ASSERT(use_gui);
1348 return LyX::ref().topLevelKeymap();
1352 IconvProcessor & utf8ToUcs4()
1354 return LyX::ref().iconvProcessor();