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"
45 #include "ToolbarBackend.h"
47 #include "frontends/Alert.h"
48 #include "frontends/Application.h"
49 #include "frontends/Gui.h"
50 #include "frontends/LyXView.h"
52 #include "support/environment.h"
53 #include "support/filetools.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>
74 using support::addName;
75 using support::addPath;
76 using support::bformat;
77 using support::createDirectory;
78 using support::createLyXTmpDir;
79 using support::destroyDir;
80 using support::FileName;
81 using support::fileSearch;
82 using support::getEnv;
83 using support::i18nLibFileSearch;
84 using support::libFileSearch;
85 using support::package;
86 using support::prependEnvPath;
88 using support::Systemcall;
90 namespace Alert = frontend::Alert;
91 namespace os = support::os;
92 namespace fs = boost::filesystem;
101 #ifndef CXX_GLOBAL_CSTD
108 /// are we using the GUI at all?
110 * We default to true and this is changed to false when the export feature is used.
117 // Filled with the command line arguments "foo" of "-sysdir foo" or
119 string cl_system_support;
120 string cl_user_support;
122 std::string geometryArg;
124 LyX * singleton_ = 0;
126 void showFileError(string const & error)
128 Alert::warning(_("Could not read configuration file"),
129 bformat(_("Error while reading the configuration file\n%1$s.\n"
130 "Please check your installation."), from_utf8(error)));
134 void reconfigureUserLyXDir()
136 string const configure_command = package().configure_command();
138 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
139 support::Path p(package().user_support());
141 one.startscript(Systemcall::Wait, configure_command);
142 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
148 /// The main application class private implementation.
149 struct LyX::Singletons
151 Singletons(): iconv(ucs4_codeset, "UTF-8")
153 // Set the default User Interface language as soon as possible.
154 // The language used will be derived from the environment
156 messages_["GUI"] = Messages();
158 /// our function handler
161 BufferList buffer_list_;
163 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
165 boost::scoped_ptr<LyXServer> lyx_server_;
167 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
169 boost::scoped_ptr<frontend::Application> application_;
170 /// lyx session, containing lastfiles, lastfilepos, and lastopened
171 boost::scoped_ptr<Session> session_;
174 IconvProcessor iconv;
176 /// Files to load at start.
177 vector<FileName> files_to_load_;
179 /// The messages translators.
180 map<string, Messages> messages_;
182 /// The file converters.
183 Converters converters_;
185 // The system converters copy after reading lyxrc.defaults.
186 Converters system_converters_;
190 frontend::Application * theApp()
193 return &singleton_->application();
206 BOOST_ASSERT(singleton_);
211 LyX const & LyX::cref()
213 BOOST_ASSERT(singleton_);
222 pimpl_.reset(new Singletons);
227 BufferList & LyX::bufferList()
229 return pimpl_->buffer_list_;
233 BufferList const & LyX::bufferList() const
235 return pimpl_->buffer_list_;
239 Session & LyX::session()
241 BOOST_ASSERT(pimpl_->session_.get());
242 return *pimpl_->session_.get();
246 Session const & LyX::session() const
248 BOOST_ASSERT(pimpl_->session_.get());
249 return *pimpl_->session_.get();
253 LyXFunc & LyX::lyxFunc()
255 return pimpl_->lyxfunc_;
259 LyXFunc const & LyX::lyxFunc() const
261 return pimpl_->lyxfunc_;
265 LyXServer & LyX::server()
267 BOOST_ASSERT(pimpl_->lyx_server_.get());
268 return *pimpl_->lyx_server_.get();
272 LyXServer const & LyX::server() const
274 BOOST_ASSERT(pimpl_->lyx_server_.get());
275 return *pimpl_->lyx_server_.get();
279 LyXServerSocket & LyX::socket()
281 BOOST_ASSERT(pimpl_->lyx_socket_.get());
282 return *pimpl_->lyx_socket_.get();
286 LyXServerSocket const & LyX::socket() const
288 BOOST_ASSERT(pimpl_->lyx_socket_.get());
289 return *pimpl_->lyx_socket_.get();
293 frontend::Application & LyX::application()
295 BOOST_ASSERT(pimpl_->application_.get());
296 return *pimpl_->application_.get();
300 frontend::Application const & LyX::application() const
302 BOOST_ASSERT(pimpl_->application_.get());
303 return *pimpl_->application_.get();
307 kb_keymap & LyX::topLevelKeymap()
309 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
310 return *pimpl_->toplevel_keymap_.get();
314 Converters & LyX::converters()
316 return pimpl_->converters_;
320 Converters & LyX::systemConverters()
322 return pimpl_->system_converters_;
326 IconvProcessor & LyX::iconvProcessor()
328 return pimpl_->iconv;
332 kb_keymap const & LyX::topLevelKeymap() const
334 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
335 return *pimpl_->toplevel_keymap_.get();
339 Messages & LyX::getMessages(std::string const & language)
341 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
343 if (it != pimpl_->messages_.end())
346 std::pair<map<string, Messages>::iterator, bool> result =
347 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
349 BOOST_ASSERT(result.second);
350 return result.first->second;
354 Messages & LyX::getGuiMessages()
356 return pimpl_->messages_["GUI"];
360 void LyX::setGuiLanguage(std::string const & language)
362 pimpl_->messages_["GUI"] = Messages(language);
366 Buffer const * const LyX::updateInset(InsetBase const * inset) const
371 Buffer const * buffer_ptr = 0;
372 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
373 vector<int>::const_iterator it = view_ids.begin();
374 vector<int>::const_iterator const end = view_ids.end();
375 for (; it != end; ++it) {
377 pimpl_->application_->gui().view(*it).updateInset(inset);
385 int LyX::exec(int & argc, char * argv[])
387 // Here we need to parse the command line. At least
388 // we need to parse for "-dbg" and "-help"
389 easyParse(argc, argv);
391 support::init_package(to_utf8(from_local8bit(argv[0])),
392 cl_system_support, cl_user_support,
393 support::top_build_dir_is_one_level_up);
396 // FIXME: create a ConsoleApplication
397 int exit_status = init(argc, argv);
405 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
410 BufferList::iterator begin = pimpl_->buffer_list_.begin();
411 BufferList::iterator end = pimpl_->buffer_list_.end();
413 bool final_success = false;
414 for (BufferList::iterator I = begin; I != end; ++I) {
416 bool success = false;
417 buf->dispatch(batch_command, &success);
418 final_success |= success;
421 return !final_success;
424 // Force adding of font path _before_ Application is initialized
425 support::os::addFontResources();
427 // Let the frontend parse and remove all arguments that it knows
428 pimpl_->application_.reset(createApplication(argc, argv));
432 // Parse and remove all known arguments in the LyX singleton
433 // Give an error for all remaining ones.
434 int exit_status = init(argc, argv);
436 // Kill the application object before exiting.
437 pimpl_->application_.reset();
444 /* Create a CoreApplication class that will provide the main event loop
445 * and the socket callback registering. With Qt4, only QtCore
446 * library would be needed.
447 * When this is done, a server_mode could be created and the following two
448 * line would be moved out from here.
450 // Note: socket callback must be registered after init(argc, argv)
451 // such that package().temp_dir() is properly initialized.
452 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
453 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
454 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
456 // Start the real execution loop.
457 exit_status = pimpl_->application_->exec();
461 // Restore original font resources after Application is destroyed.
462 support::os::restoreFontResources();
468 void LyX::prepareExit()
470 // Set a flag that we do quitting from the program,
471 // so no refreshes are necessary.
474 // close buffers first
475 pimpl_->buffer_list_.closeAll();
477 // do any other cleanup procedures now
478 if (package().temp_dir() != package().system_temp_dir()) {
479 lyxerr[Debug::INFO] << "Deleting tmp dir "
480 << package().temp_dir() << endl;
482 if (!destroyDir(FileName(package().temp_dir()))) {
483 docstring const msg =
484 bformat(_("Unable to remove the temporary directory %1$s"),
485 from_utf8(package().temp_dir()));
486 Alert::warning(_("Unable to remove temporary directory"), msg);
491 if (pimpl_->session_)
492 pimpl_->session_->writeFile();
493 pimpl_->session_.reset();
494 pimpl_->lyx_server_.reset();
495 pimpl_->lyx_socket_.reset();
498 // Kill the application object before exiting. This avoids crashes
499 // when exiting on Linux.
500 if (pimpl_->application_)
501 pimpl_->application_.reset();
505 void LyX::earlyExit(int status)
507 BOOST_ASSERT(pimpl_->application_.get());
508 // LyX::pimpl_::application_ is not initialised at this
509 // point so it's safe to just exit after some cleanup.
515 int LyX::init(int & argc, char * argv[])
517 // check for any spurious extra arguments
518 // other than documents
519 for (int argi = 1; argi < argc ; ++argi) {
520 if (argv[argi][0] == '-') {
522 bformat(_("Wrong command line option `%1$s'. Exiting."),
523 from_utf8(argv[argi]))) << endl;
528 // Initialization of LyX (reads lyxrc and more)
529 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
530 bool success = init();
531 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
535 for (int argi = argc - 1; argi >= 1; --argi) {
536 // get absolute path of file and add ".lyx" to
537 // the filename if necessary
538 pimpl_->files_to_load_.push_back(fileSearch(string(),
539 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
540 "lyx", support::allow_unreadable));
544 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
550 void LyX::loadFiles()
552 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
553 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
555 for (; it != end; ++it) {
559 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
560 if (loadLyXFile(buf, *it)) {
561 ErrorList const & el = buf->errorList("Parse");
563 for_each(el.begin(), el.end(),
564 boost::bind(&LyX::printError, this, _1));
567 pimpl_->buffer_list_.release(buf);
572 void LyX::execBatchCommands()
574 // The advantage of doing this here is that the event loop
575 // is already started. So any need for interaction will be
579 // Execute batch commands if available
580 if (batch_command.empty())
583 lyxerr[Debug::INIT] << "About to handle -x '"
584 << batch_command << '\'' << endl;
586 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
590 void LyX::restoreGuiSession()
592 LyXView * view = newLyXView();
594 // if some files were specified at command-line we assume that the
595 // user wants to edit *these* files and not to restore the session.
596 if (!pimpl_->files_to_load_.empty()) {
597 for_each(pimpl_->files_to_load_.begin(),
598 pimpl_->files_to_load_.end(),
599 bind(&LyXView::loadLyXFile, view, _1, true));
600 // clear this list to save a few bytes of RAM
601 pimpl_->files_to_load_.clear();
602 pimpl_->session_->lastOpened().clear();
606 if (!lyxrc.load_session)
609 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
610 // do not add to the lastfile list since these files are restored from
611 // last session, and should be already there (regular files), or should
612 // not be added at all (help files).
613 for_each(lastopened.begin(), lastopened.end(),
614 bind(&LyXView::loadLyXFile, view, _1, false));
616 // clear this list to save a few bytes of RAM
617 pimpl_->session_->lastOpened().clear();
621 LyXView * LyX::newLyXView()
626 // determine windows size and position, from lyxrc and/or session
628 unsigned int width = 690;
629 unsigned int height = 510;
630 // default icon size, will be overwritten by stored session value
631 unsigned int iconSizeXY = 0;
632 bool maximize = false;
634 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
635 width = lyxrc.geometry_width;
636 height = lyxrc.geometry_height;
638 // if lyxrc returns (0,0), then use session info
640 string val = session().sessionInfo().load("WindowWidth");
642 width = convert<unsigned int>(val);
643 val = session().sessionInfo().load("WindowHeight");
645 height = convert<unsigned int>(val);
646 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
648 val = session().sessionInfo().load("IconSizeXY");
650 iconSizeXY = convert<unsigned int>(val);
653 // if user wants to restore window position
656 if (lyxrc.geometry_xysaved) {
657 string val = session().sessionInfo().load("WindowPosX");
659 posx = convert<int>(val);
660 val = session().sessionInfo().load("WindowPosY");
662 posy = convert<int>(val);
665 if (!geometryArg.empty())
671 // create the main window
672 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
680 The SIGHUP signal does not exist on Windows and does not need to be handled.
682 Windows handles SIGFPE and SIGSEGV signals as expected.
684 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
685 cause a new thread to be spawned. This may well result in unexpected
686 behaviour by the single-threaded LyX.
688 SIGTERM signals will come only from another process actually sending
689 that signal using 'raise' in Windows' POSIX compatability layer. It will
690 not come from the general "terminate process" methods that everyone
691 actually uses (and which can't be trapped). Killing an app 'politely' on
692 Windows involves first sending a WM_CLOSE message, something that is
693 caught already by the Qt frontend.
695 For more information see:
697 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
698 ...signals are mostly useless on Windows for a variety of reasons that are
701 'UNIX Application Migration Guide, Chapter 9'
702 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
704 'How To Terminate an Application "Cleanly" in Win32'
705 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
709 static void error_handler(int err_sig)
711 // Throw away any signals other than the first one received.
712 static sig_atomic_t handling_error = false;
715 handling_error = true;
717 // We have received a signal indicating a fatal error, so
718 // try and save the data ASAP.
719 LyX::cref().emergencyCleanup();
721 // These lyxerr calls may or may not work:
723 // Signals are asynchronous, so the main program may be in a very
724 // fragile state when a signal is processed and thus while a signal
725 // handler function executes.
726 // In general, therefore, we should avoid performing any
727 // I/O operations or calling most library and system functions from
730 // This shouldn't matter here, however, as we've already invoked
735 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
739 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
742 lyxerr << "\nlyx: SIGSEGV signal caught\n"
743 "Sorry, you have found a bug in LyX. "
744 "Please read the bug-reporting instructions "
745 "in Help->Introduction and send us a bug report, "
746 "if necessary. Thanks !\nBye." << endl;
754 // Deinstall the signal handlers
756 signal(SIGHUP, SIG_DFL);
758 signal(SIGINT, SIG_DFL);
759 signal(SIGFPE, SIG_DFL);
760 signal(SIGSEGV, SIG_DFL);
761 signal(SIGTERM, SIG_DFL);
764 if (err_sig == SIGSEGV ||
765 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
767 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
776 void LyX::printError(ErrorItem const & ei)
778 docstring tmp = _("LyX: ") + ei.error + char_type(':')
780 std::cerr << to_utf8(tmp) << std::endl;
784 void LyX::initGuiFont()
786 if (lyxrc.roman_font_name.empty())
787 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
789 if (lyxrc.sans_font_name.empty())
790 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
792 if (lyxrc.typewriter_font_name.empty())
793 lyxrc.typewriter_font_name
794 = pimpl_->application_->typewriterFontName();
801 signal(SIGHUP, error_handler);
803 signal(SIGFPE, error_handler);
804 signal(SIGSEGV, error_handler);
805 signal(SIGINT, error_handler);
806 signal(SIGTERM, error_handler);
807 // SIGPIPE can be safely ignored.
809 lyxrc.tempdir_path = package().temp_dir();
810 lyxrc.document_path = package().document_dir();
812 if (lyxrc.template_path.empty()) {
813 lyxrc.template_path = addPath(package().system_support(),
818 // Read configuration files
821 // This one may have been distributed along with LyX.
822 if (!readRcFile("lyxrc.dist"))
825 // Set the language defined by the distributor.
826 //setGuiLanguage(lyxrc.gui_language);
828 // Set the PATH correctly.
829 #if !defined (USE_POSIX_PACKAGING)
830 // Add the directory containing the LyX executable to the path
831 // so that LyX can find things like tex2lyx.
832 if (package().build_support().empty())
833 prependEnvPath("PATH", package().binary_dir());
835 if (!lyxrc.path_prefix.empty())
836 prependEnvPath("PATH", lyxrc.path_prefix);
838 // Check that user LyX directory is ok.
839 if (queryUserLyXDir(package().explicit_user_support()))
840 reconfigureUserLyXDir();
842 // no need for a splash when there is no GUI
847 // This one is generated in user_support directory by lib/configure.py.
848 if (!readRcFile("lyxrc.defaults"))
851 // Query the OS to know what formats are viewed natively
852 formats.setAutoOpen();
854 // Read lyxrc.dist again to be able to override viewer auto-detection.
855 readRcFile("lyxrc.dist");
857 system_lyxrc = lyxrc;
858 system_formats = formats;
859 pimpl_->system_converters_ = pimpl_->converters_;
860 system_movers = movers;
861 system_lcolor = lcolor;
863 // This one is edited through the preferences dialog.
864 if (!readRcFile("preferences"))
867 if (!readEncodingsFile("encodings"))
869 if (!readLanguagesFile("languages"))
873 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
878 // Set the language defined by the user.
879 //setGuiLanguage(lyxrc.gui_language);
882 pimpl_->toplevel_keymap_.reset(new kb_keymap);
883 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
884 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
886 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
889 if (!readUIFile(lyxrc.ui_file))
893 if (lyxerr.debugging(Debug::LYXRC))
896 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
897 if (!lyxrc.path_prefix.empty())
898 prependEnvPath("PATH", lyxrc.path_prefix);
900 FileName const document_path(lyxrc.document_path);
901 if (fs::exists(document_path.toFilesystemEncoding()) &&
902 fs::is_directory(document_path.toFilesystemEncoding()))
903 package().document_dir() = lyxrc.document_path;
905 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
906 if (package().temp_dir().empty()) {
907 Alert::error(_("Could not create temporary directory"),
908 bformat(_("Could not create a temporary directory in\n"
909 "%1$s. Make sure that this\n"
910 "path exists and is writable and try again."),
911 from_utf8(lyxrc.tempdir_path)));
912 // createLyXTmpDir() tries sufficiently hard to create a
913 // usable temp dir, so the probability to come here is
914 // close to zero. We therefore don't try to overcome this
915 // problem with e.g. asking the user for a new path and
916 // trying again but simply exit.
920 if (lyxerr.debugging(Debug::INIT)) {
921 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
924 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
925 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
927 // This must happen after package initialization and after lyxrc is
928 // read, therefore it can't be done by a static object.
929 ConverterCache::init();
935 void LyX::defaultKeyBindings(kb_keymap * kbmap)
937 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
938 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
939 kbmap->bind("Up", FuncRequest(LFUN_UP));
940 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
942 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
943 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
944 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
945 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
947 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
948 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
949 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
950 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
952 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
953 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
955 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
956 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
958 // kbmap->bindings to enable the use of the numeric keypad
960 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
961 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
962 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
963 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
964 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
965 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
966 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
967 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
968 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
969 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
970 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
971 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
972 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
973 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
974 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
975 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
976 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
977 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
978 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
979 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
980 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
981 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
982 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
983 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
987 void LyX::emergencyCleanup() const
989 // what to do about tmpfiles is non-obvious. we would
990 // like to delete any we find, but our lyxdir might
991 // contain documents etc. which might be helpful on
994 pimpl_->buffer_list_.emergencyWriteAll();
996 if (pimpl_->lyx_server_)
997 pimpl_->lyx_server_->emergencyCleanup();
998 pimpl_->lyx_server_.reset();
999 pimpl_->lyx_socket_.reset();
1004 void LyX::deadKeyBindings(kb_keymap * kbmap)
1006 // bindKeyings for transparent handling of deadkeys
1007 // The keysyms are gotten from XFree86 X11R6
1008 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1009 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1010 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1011 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1012 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1013 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1014 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1015 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1016 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1017 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1018 // nothing with this name
1019 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1020 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1021 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1022 // nothing with this name either...
1023 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1024 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1025 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1026 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1032 // return true if file does not exist or is older than configure.py.
1033 bool needsUpdate(string const & file)
1035 // We cannot initialize configure_script directly because the package
1036 // is not initialized yet when static objects are constructed.
1037 static string configure_script;
1038 static bool firstrun = true;
1040 configure_script = FileName(addName(
1041 package().system_support(),
1042 "configure.py")).toFilesystemEncoding();
1046 string const absfile = FileName(addName(
1047 package().user_support(), file)).toFilesystemEncoding();
1048 return (! fs::exists(absfile))
1049 || (fs::last_write_time(configure_script)
1050 > fs::last_write_time(absfile));
1056 bool LyX::queryUserLyXDir(bool explicit_userdir)
1058 // Does user directory exist?
1059 string const user_support =
1060 FileName(package().user_support()).toFilesystemEncoding();
1061 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1062 first_start = false;
1064 return needsUpdate("lyxrc.defaults")
1065 || needsUpdate("textclass.lst")
1066 || needsUpdate("packages.lst");
1069 first_start = !explicit_userdir;
1071 // If the user specified explicitly a directory, ask whether
1072 // to create it. If the user says "no", then exit.
1073 if (explicit_userdir &&
1075 _("Missing user LyX directory"),
1076 bformat(_("You have specified a non-existent user "
1077 "LyX directory, %1$s.\n"
1078 "It is needed to keep your own configuration."),
1079 from_utf8(package().user_support())),
1081 _("&Create directory"),
1083 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1084 earlyExit(EXIT_FAILURE);
1087 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1088 from_utf8(package().user_support())))
1091 if (!createDirectory(package().user_support(), 0755)) {
1092 // Failed, so let's exit.
1093 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1095 earlyExit(EXIT_FAILURE);
1102 bool LyX::readRcFile(string const & name)
1104 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1106 FileName const lyxrc_path = libFileSearch(string(), name);
1107 if (!lyxrc_path.empty()) {
1109 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1111 if (lyxrc.read(lyxrc_path) < 0) {
1112 showFileError(name);
1116 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1122 // Read the ui file `name'
1123 bool LyX::readUIFile(string const & name)
1133 struct keyword_item uitags[ui_last - 1] = {
1134 { "include", ui_include },
1135 { "menuset", ui_menuset },
1136 { "toolbar", ui_toolbar },
1137 { "toolbars", ui_toolbars }
1140 // Ensure that a file is read only once (prevents include loops)
1141 static std::list<string> uifiles;
1142 std::list<string>::const_iterator it = uifiles.begin();
1143 std::list<string>::const_iterator end = uifiles.end();
1144 it = std::find(it, end, name);
1146 lyxerr[Debug::INIT] << "UI file '" << name
1147 << "' has been read already. "
1148 << "Is this an include loop?"
1153 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1155 FileName const ui_path = libFileSearch("ui", name, "ui");
1157 if (ui_path.empty()) {
1158 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1159 showFileError(name);
1162 uifiles.push_back(name);
1164 lyxerr[Debug::INIT] << "Found " << name
1165 << " in " << ui_path << endl;
1166 LyXLex lex(uitags, ui_last - 1);
1167 lex.setFile(ui_path);
1169 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1173 if (lyxerr.debugging(Debug::PARSER))
1174 lex.printTable(lyxerr);
1176 while (lex.isOK()) {
1177 switch (lex.lex()) {
1180 string const file = lex.getString();
1181 if (!readUIFile(file))
1186 menubackend.read(lex);
1190 toolbarbackend.read(lex);
1194 toolbarbackend.readToolbars(lex);
1198 if (!rtrim(lex.getString()).empty())
1199 lex.printError("LyX::ReadUIFile: "
1200 "Unknown menu tag: `$$Token'");
1208 // Read the languages file `name'
1209 bool LyX::readLanguagesFile(string const & name)
1211 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1213 FileName const lang_path = libFileSearch(string(), name);
1214 if (lang_path.empty()) {
1215 showFileError(name);
1218 languages.read(lang_path);
1223 // Read the encodings file `name'
1224 bool LyX::readEncodingsFile(string const & name)
1226 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1228 FileName const enc_path = libFileSearch(string(), name);
1229 if (enc_path.empty()) {
1230 showFileError(name);
1233 encodings.read(enc_path);
1242 /// return the the number of arguments consumed
1243 typedef boost::function<int(string const &, string const &)> cmd_helper;
1245 int parse_dbg(string const & arg, string const &)
1248 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1249 Debug::showTags(lyxerr);
1252 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1254 lyxerr.level(Debug::value(arg));
1255 Debug::showLevel(lyxerr, lyxerr.level());
1260 int parse_help(string const &, string const &)
1263 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1264 "Command line switches (case sensitive):\n"
1265 "\t-help summarize LyX usage\n"
1266 "\t-userdir dir set user directory to dir\n"
1267 "\t-sysdir dir set system directory to dir\n"
1268 "\t-geometry WxH+X+Y set geometry of the main window\n"
1269 "\t-dbg feature[,feature]...\n"
1270 " select the features to debug.\n"
1271 " Type `lyx -dbg' to see the list of features\n"
1272 "\t-x [--execute] command\n"
1273 " where command is a lyx command.\n"
1274 "\t-e [--export] fmt\n"
1275 " where fmt is the export format of choice.\n"
1276 "\t-i [--import] fmt file.xxx\n"
1277 " where fmt is the import format of choice\n"
1278 " and file.xxx is the file to be imported.\n"
1279 "\t-version summarize version and build info\n"
1280 "Check the LyX man page for more details.")) << endl;
1285 int parse_version(string const &, string const &)
1287 lyxerr << "LyX " << lyx_version
1288 << " (" << lyx_release_date << ")" << endl;
1289 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1291 lyxerr << lyx_version_info << endl;
1296 int parse_sysdir(string const & arg, string const &)
1299 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1302 cl_system_support = arg;
1306 int parse_userdir(string const & arg, string const &)
1309 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1312 cl_user_support = arg;
1316 int parse_execute(string const & arg, string const &)
1319 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1326 int parse_export(string const & type, string const &)
1329 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1330 "--export switch")) << endl;
1333 batch = "buffer-export " + type;
1338 int parse_import(string const & type, string const & file)
1341 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1342 "--import switch")) << endl;
1346 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1350 batch = "buffer-import " + type + ' ' + file;
1354 int parse_geometry(string const & arg1, string const &)
1357 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1358 // remove also the arg
1361 // don't remove "-geometry"
1370 void LyX::easyParse(int & argc, char * argv[])
1372 std::map<string, cmd_helper> cmdmap;
1374 cmdmap["-dbg"] = parse_dbg;
1375 cmdmap["-help"] = parse_help;
1376 cmdmap["--help"] = parse_help;
1377 cmdmap["-version"] = parse_version;
1378 cmdmap["--version"] = parse_version;
1379 cmdmap["-sysdir"] = parse_sysdir;
1380 cmdmap["-userdir"] = parse_userdir;
1381 cmdmap["-x"] = parse_execute;
1382 cmdmap["--execute"] = parse_execute;
1383 cmdmap["-e"] = parse_export;
1384 cmdmap["--export"] = parse_export;
1385 cmdmap["-i"] = parse_import;
1386 cmdmap["--import"] = parse_import;
1387 cmdmap["-geometry"] = parse_geometry;
1389 for (int i = 1; i < argc; ++i) {
1390 std::map<string, cmd_helper>::const_iterator it
1391 = cmdmap.find(argv[i]);
1393 // don't complain if not found - may be parsed later
1394 if (it == cmdmap.end())
1397 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1398 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1400 int const remove = 1 + it->second(arg, arg2);
1402 // Now, remove used arguments by shifting
1403 // the following ones remove places down.
1406 for (int j = i; j < argc; ++j)
1407 argv[j] = argv[j + remove];
1412 batch_command = batch;
1416 FuncStatus getStatus(FuncRequest const & action)
1418 return LyX::ref().lyxFunc().getStatus(action);
1422 void dispatch(FuncRequest const & action)
1424 LyX::ref().lyxFunc().dispatch(action);
1428 BufferList & theBufferList()
1430 return LyX::ref().bufferList();
1434 LyXFunc & theLyXFunc()
1436 return LyX::ref().lyxFunc();
1440 LyXServer & theLyXServer()
1442 // FIXME: this should not be use_gui dependent
1443 BOOST_ASSERT(use_gui);
1444 return LyX::ref().server();
1448 LyXServerSocket & theLyXServerSocket()
1450 // FIXME: this should not be use_gui dependent
1451 BOOST_ASSERT(use_gui);
1452 return LyX::ref().socket();
1456 kb_keymap & theTopLevelKeymap()
1458 BOOST_ASSERT(use_gui);
1459 return LyX::ref().topLevelKeymap();
1463 Converters & theConverters()
1465 return LyX::ref().converters();
1469 Converters & theSystemConverters()
1471 return LyX::ref().systemConverters();
1475 IconvProcessor & utf8ToUcs4()
1477 return LyX::ref().iconvProcessor();
1481 Messages & getMessages(std::string const & language)
1483 return LyX::ref().getMessages(language);
1487 Messages & getGuiMessages()
1489 return LyX::ref().getGuiMessages();