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::changeExtension;
78 using support::createDirectory;
79 using support::createLyXTmpDir;
80 using support::destroyDir;
81 using support::FileName;
82 using support::fileSearch;
83 using support::getEnv;
84 using support::i18nLibFileSearch;
85 using support::libFileSearch;
86 using support::package;
87 using support::prependEnvPath;
89 using support::Systemcall;
91 namespace Alert = frontend::Alert;
92 namespace os = support::os;
93 namespace fs = boost::filesystem;
102 #ifndef CXX_GLOBAL_CSTD
109 /// are we using the GUI at all?
111 * We default to true and this is changed to false when the export feature is used.
118 // Filled with the command line arguments "foo" of "-sysdir foo" or
120 string cl_system_support;
121 string cl_user_support;
123 std::string geometryArg;
125 LyX * singleton_ = 0;
127 void showFileError(string const & error)
129 Alert::warning(_("Could not read configuration file"),
130 bformat(_("Error while reading the configuration file\n%1$s.\n"
131 "Please check your installation."), from_utf8(error)));
135 void reconfigureUserLyXDir()
137 string const configure_command = package().configure_command();
139 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
140 support::Path p(package().user_support());
142 one.startscript(Systemcall::Wait, configure_command);
143 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
149 /// The main application class private implementation.
150 struct LyX::Singletons
152 Singletons(): iconv(ucs4_codeset, "UTF-8")
154 // Set the default User Interface language as soon as possible.
155 // The language used will be derived from the environment
157 messages_["GUI"] = Messages();
159 /// our function handler
162 BufferList buffer_list_;
164 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
166 boost::scoped_ptr<LyXServer> lyx_server_;
168 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
170 boost::scoped_ptr<frontend::Application> application_;
171 /// lyx session, containing lastfiles, lastfilepos, and lastopened
172 boost::scoped_ptr<Session> session_;
175 IconvProcessor iconv;
177 /// Files to load at start.
178 vector<FileName> files_to_load_;
180 /// The messages translators.
181 map<string, Messages> messages_;
183 /// The file converters.
184 Converters converters_;
186 // The system converters copy after reading lyxrc.defaults.
187 Converters system_converters_;
193 Movers system_movers_;
197 frontend::Application * theApp()
200 return &singleton_->application();
213 BOOST_ASSERT(singleton_);
218 LyX const & LyX::cref()
220 BOOST_ASSERT(singleton_);
229 pimpl_.reset(new Singletons);
234 BufferList & LyX::bufferList()
236 return pimpl_->buffer_list_;
240 BufferList const & LyX::bufferList() const
242 return pimpl_->buffer_list_;
246 Session & LyX::session()
248 BOOST_ASSERT(pimpl_->session_.get());
249 return *pimpl_->session_.get();
253 Session const & LyX::session() const
255 BOOST_ASSERT(pimpl_->session_.get());
256 return *pimpl_->session_.get();
260 LyXFunc & LyX::lyxFunc()
262 return pimpl_->lyxfunc_;
266 LyXFunc const & LyX::lyxFunc() const
268 return pimpl_->lyxfunc_;
272 LyXServer & LyX::server()
274 BOOST_ASSERT(pimpl_->lyx_server_.get());
275 return *pimpl_->lyx_server_.get();
279 LyXServer const & LyX::server() const
281 BOOST_ASSERT(pimpl_->lyx_server_.get());
282 return *pimpl_->lyx_server_.get();
286 LyXServerSocket & LyX::socket()
288 BOOST_ASSERT(pimpl_->lyx_socket_.get());
289 return *pimpl_->lyx_socket_.get();
293 LyXServerSocket const & LyX::socket() const
295 BOOST_ASSERT(pimpl_->lyx_socket_.get());
296 return *pimpl_->lyx_socket_.get();
300 frontend::Application & LyX::application()
302 BOOST_ASSERT(pimpl_->application_.get());
303 return *pimpl_->application_.get();
307 frontend::Application const & LyX::application() const
309 BOOST_ASSERT(pimpl_->application_.get());
310 return *pimpl_->application_.get();
314 kb_keymap & LyX::topLevelKeymap()
316 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
317 return *pimpl_->toplevel_keymap_.get();
321 Converters & LyX::converters()
323 return pimpl_->converters_;
327 Converters & LyX::systemConverters()
329 return pimpl_->system_converters_;
333 IconvProcessor & LyX::iconvProcessor()
335 return pimpl_->iconv;
339 kb_keymap const & LyX::topLevelKeymap() const
341 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
342 return *pimpl_->toplevel_keymap_.get();
346 Messages & LyX::getMessages(std::string const & language)
348 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
350 if (it != pimpl_->messages_.end())
353 std::pair<map<string, Messages>::iterator, bool> result =
354 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
356 BOOST_ASSERT(result.second);
357 return result.first->second;
361 Messages & LyX::getGuiMessages()
363 return pimpl_->messages_["GUI"];
367 void LyX::setGuiLanguage(std::string const & language)
369 pimpl_->messages_["GUI"] = Messages(language);
373 Buffer const * const LyX::updateInset(InsetBase const * inset) const
378 Buffer const * buffer_ptr = 0;
379 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
380 vector<int>::const_iterator it = view_ids.begin();
381 vector<int>::const_iterator const end = view_ids.end();
382 for (; it != end; ++it) {
384 pimpl_->application_->gui().view(*it).updateInset(inset);
392 int LyX::exec(int & argc, char * argv[])
394 // Here we need to parse the command line. At least
395 // we need to parse for "-dbg" and "-help"
396 easyParse(argc, argv);
398 support::init_package(to_utf8(from_local8bit(argv[0])),
399 cl_system_support, cl_user_support,
400 support::top_build_dir_is_one_level_up);
403 // FIXME: create a ConsoleApplication
404 int exit_status = init(argc, argv);
412 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
417 BufferList::iterator begin = pimpl_->buffer_list_.begin();
418 BufferList::iterator end = pimpl_->buffer_list_.end();
420 bool final_success = false;
421 for (BufferList::iterator I = begin; I != end; ++I) {
423 bool success = false;
424 buf->dispatch(batch_command, &success);
425 final_success |= success;
428 return !final_success;
431 // Force adding of font path _before_ Application is initialized
432 support::os::addFontResources();
434 // Let the frontend parse and remove all arguments that it knows
435 pimpl_->application_.reset(createApplication(argc, argv));
439 // Parse and remove all known arguments in the LyX singleton
440 // Give an error for all remaining ones.
441 int exit_status = init(argc, argv);
443 // Kill the application object before exiting.
444 pimpl_->application_.reset();
451 /* Create a CoreApplication class that will provide the main event loop
452 * and the socket callback registering. With Qt4, only QtCore
453 * library would be needed.
454 * When this is done, a server_mode could be created and the following two
455 * line would be moved out from here.
457 // Note: socket callback must be registered after init(argc, argv)
458 // such that package().temp_dir() is properly initialized.
459 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
460 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
461 os::internal_path(package().temp_dir() + "/lyxsocket")));
463 // Start the real execution loop.
464 exit_status = pimpl_->application_->exec();
468 // Restore original font resources after Application is destroyed.
469 support::os::restoreFontResources();
475 void LyX::prepareExit()
477 // Set a flag that we do quitting from the program,
478 // so no refreshes are necessary.
481 // close buffers first
482 pimpl_->buffer_list_.closeAll();
484 // do any other cleanup procedures now
485 if (package().temp_dir() != package().system_temp_dir()) {
486 lyxerr[Debug::INFO] << "Deleting tmp dir "
487 << package().temp_dir() << endl;
489 if (!destroyDir(FileName(package().temp_dir()))) {
490 docstring const msg =
491 bformat(_("Unable to remove the temporary directory %1$s"),
492 from_utf8(package().temp_dir()));
493 Alert::warning(_("Unable to remove temporary directory"), msg);
498 if (pimpl_->session_)
499 pimpl_->session_->writeFile();
500 pimpl_->session_.reset();
501 pimpl_->lyx_server_.reset();
502 pimpl_->lyx_socket_.reset();
505 // Kill the application object before exiting. This avoids crashes
506 // when exiting on Linux.
507 if (pimpl_->application_)
508 pimpl_->application_.reset();
512 void LyX::earlyExit(int status)
514 BOOST_ASSERT(pimpl_->application_.get());
515 // LyX::pimpl_::application_ is not initialised at this
516 // point so it's safe to just exit after some cleanup.
522 int LyX::init(int & argc, char * argv[])
524 // check for any spurious extra arguments
525 // other than documents
526 for (int argi = 1; argi < argc ; ++argi) {
527 if (argv[argi][0] == '-') {
529 bformat(_("Wrong command line option `%1$s'. Exiting."),
530 from_utf8(argv[argi]))) << endl;
535 // Initialization of LyX (reads lyxrc and more)
536 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
537 bool success = init();
538 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
542 for (int argi = argc - 1; argi >= 1; --argi) {
543 // get absolute path of file and add ".lyx" to
544 // the filename if necessary
545 pimpl_->files_to_load_.push_back(fileSearch(string(),
546 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
547 "lyx", support::allow_unreadable));
551 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
557 void LyX::loadFiles()
559 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
560 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
562 for (; it != end; ++it) {
566 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
567 if (loadLyXFile(buf, *it)) {
568 ErrorList const & el = buf->errorList("Parse");
570 for_each(el.begin(), el.end(),
571 boost::bind(&LyX::printError, this, _1));
574 pimpl_->buffer_list_.release(buf);
579 void LyX::execBatchCommands()
581 // The advantage of doing this here is that the event loop
582 // is already started. So any need for interaction will be
586 // Execute batch commands if available
587 if (batch_command.empty())
590 lyxerr[Debug::INIT] << "About to handle -x '"
591 << batch_command << '\'' << endl;
593 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
597 void LyX::restoreGuiSession()
599 LyXView * view = newLyXView();
601 // if some files were specified at command-line we assume that the
602 // user wants to edit *these* files and not to restore the session.
603 if (!pimpl_->files_to_load_.empty()) {
604 for_each(pimpl_->files_to_load_.begin(),
605 pimpl_->files_to_load_.end(),
606 bind(&LyXView::loadLyXFile, view, _1, true));
607 // clear this list to save a few bytes of RAM
608 pimpl_->files_to_load_.clear();
609 pimpl_->session_->lastOpened().clear();
613 if (!lyxrc.load_session)
616 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
617 // do not add to the lastfile list since these files are restored from
618 // last session, and should be already there (regular files), or should
619 // not be added at all (help files).
620 for_each(lastopened.begin(), lastopened.end(),
621 bind(&LyXView::loadLyXFile, view, _1, false));
623 // clear this list to save a few bytes of RAM
624 pimpl_->session_->lastOpened().clear();
628 LyXView * LyX::newLyXView()
633 // determine windows size and position, from lyxrc and/or session
635 unsigned int width = 690;
636 unsigned int height = 510;
637 // default icon size, will be overwritten by stored session value
638 unsigned int iconSizeXY = 0;
639 bool maximize = false;
641 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
642 width = lyxrc.geometry_width;
643 height = lyxrc.geometry_height;
645 // if lyxrc returns (0,0), then use session info
647 string val = session().sessionInfo().load("WindowWidth");
649 width = convert<unsigned int>(val);
650 val = session().sessionInfo().load("WindowHeight");
652 height = convert<unsigned int>(val);
653 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
655 val = session().sessionInfo().load("IconSizeXY");
657 iconSizeXY = convert<unsigned int>(val);
660 // if user wants to restore window position
663 if (lyxrc.geometry_xysaved) {
664 string val = session().sessionInfo().load("WindowPosX");
666 posx = convert<int>(val);
667 val = session().sessionInfo().load("WindowPosY");
669 posy = convert<int>(val);
672 if (!geometryArg.empty())
678 // create the main window
679 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
687 The SIGHUP signal does not exist on Windows and does not need to be handled.
689 Windows handles SIGFPE and SIGSEGV signals as expected.
691 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
692 cause a new thread to be spawned. This may well result in unexpected
693 behaviour by the single-threaded LyX.
695 SIGTERM signals will come only from another process actually sending
696 that signal using 'raise' in Windows' POSIX compatability layer. It will
697 not come from the general "terminate process" methods that everyone
698 actually uses (and which can't be trapped). Killing an app 'politely' on
699 Windows involves first sending a WM_CLOSE message, something that is
700 caught already by the Qt frontend.
702 For more information see:
704 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
705 ...signals are mostly useless on Windows for a variety of reasons that are
708 'UNIX Application Migration Guide, Chapter 9'
709 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
711 'How To Terminate an Application "Cleanly" in Win32'
712 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
716 static void error_handler(int err_sig)
718 // Throw away any signals other than the first one received.
719 static sig_atomic_t handling_error = false;
722 handling_error = true;
724 // We have received a signal indicating a fatal error, so
725 // try and save the data ASAP.
726 LyX::cref().emergencyCleanup();
728 // These lyxerr calls may or may not work:
730 // Signals are asynchronous, so the main program may be in a very
731 // fragile state when a signal is processed and thus while a signal
732 // handler function executes.
733 // In general, therefore, we should avoid performing any
734 // I/O operations or calling most library and system functions from
737 // This shouldn't matter here, however, as we've already invoked
742 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
746 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
749 lyxerr << "\nlyx: SIGSEGV signal caught\n"
750 "Sorry, you have found a bug in LyX. "
751 "Please read the bug-reporting instructions "
752 "in Help->Introduction and send us a bug report, "
753 "if necessary. Thanks !\nBye." << endl;
761 // Deinstall the signal handlers
763 signal(SIGHUP, SIG_DFL);
765 signal(SIGINT, SIG_DFL);
766 signal(SIGFPE, SIG_DFL);
767 signal(SIGSEGV, SIG_DFL);
768 signal(SIGTERM, SIG_DFL);
771 if (err_sig == SIGSEGV ||
772 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
774 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
783 void LyX::printError(ErrorItem const & ei)
785 docstring tmp = _("LyX: ") + ei.error + char_type(':')
787 std::cerr << to_utf8(tmp) << std::endl;
791 void LyX::initGuiFont()
793 if (lyxrc.roman_font_name.empty())
794 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
796 if (lyxrc.sans_font_name.empty())
797 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
799 if (lyxrc.typewriter_font_name.empty())
800 lyxrc.typewriter_font_name
801 = pimpl_->application_->typewriterFontName();
808 signal(SIGHUP, error_handler);
810 signal(SIGFPE, error_handler);
811 signal(SIGSEGV, error_handler);
812 signal(SIGINT, error_handler);
813 signal(SIGTERM, error_handler);
814 // SIGPIPE can be safely ignored.
816 lyxrc.tempdir_path = package().temp_dir();
817 lyxrc.document_path = package().document_dir();
819 if (lyxrc.template_path.empty()) {
820 lyxrc.template_path = addPath(package().system_support(),
825 // Read configuration files
828 // This one may have been distributed along with LyX.
829 if (!readRcFile("lyxrc.dist"))
832 // Set the language defined by the distributor.
833 //setGuiLanguage(lyxrc.gui_language);
835 // Set the PATH correctly.
836 #if !defined (USE_POSIX_PACKAGING)
837 // Add the directory containing the LyX executable to the path
838 // so that LyX can find things like tex2lyx.
839 if (package().build_support().empty())
840 prependEnvPath("PATH", package().binary_dir());
842 if (!lyxrc.path_prefix.empty())
843 prependEnvPath("PATH", lyxrc.path_prefix);
845 // Check that user LyX directory is ok.
846 if (queryUserLyXDir(package().explicit_user_support()))
847 reconfigureUserLyXDir();
849 // no need for a splash when there is no GUI
854 // This one is generated in user_support directory by lib/configure.py.
855 if (!readRcFile("lyxrc.defaults"))
858 // Query the OS to know what formats are viewed natively
859 formats.setAutoOpen();
861 // Read lyxrc.dist again to be able to override viewer auto-detection.
862 readRcFile("lyxrc.dist");
864 system_lyxrc = lyxrc;
865 system_formats = formats;
866 pimpl_->system_converters_ = pimpl_->converters_;
867 pimpl_->system_movers_ = pimpl_->movers_;
868 system_lcolor = lcolor;
870 // This one is edited through the preferences dialog.
871 if (!readRcFile("preferences"))
874 if (!readEncodingsFile("encodings"))
876 if (!readLanguagesFile("languages"))
880 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
885 // Set the language defined by the user.
886 //setGuiLanguage(lyxrc.gui_language);
889 pimpl_->toplevel_keymap_.reset(new kb_keymap);
890 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
891 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
893 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
896 if (!readUIFile(lyxrc.ui_file))
900 if (lyxerr.debugging(Debug::LYXRC))
903 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
904 if (!lyxrc.path_prefix.empty())
905 prependEnvPath("PATH", lyxrc.path_prefix);
907 FileName const document_path(lyxrc.document_path);
908 if (fs::exists(document_path.toFilesystemEncoding()) &&
909 fs::is_directory(document_path.toFilesystemEncoding()))
910 package().document_dir() = lyxrc.document_path;
912 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
913 if (package().temp_dir().empty()) {
914 Alert::error(_("Could not create temporary directory"),
915 bformat(_("Could not create a temporary directory in\n"
916 "%1$s. Make sure that this\n"
917 "path exists and is writable and try again."),
918 from_utf8(lyxrc.tempdir_path)));
919 // createLyXTmpDir() tries sufficiently hard to create a
920 // usable temp dir, so the probability to come here is
921 // close to zero. We therefore don't try to overcome this
922 // problem with e.g. asking the user for a new path and
923 // trying again but simply exit.
927 if (lyxerr.debugging(Debug::INIT)) {
928 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
931 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
932 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
934 // This must happen after package initialization and after lyxrc is
935 // read, therefore it can't be done by a static object.
936 ConverterCache::init();
942 void LyX::defaultKeyBindings(kb_keymap * kbmap)
944 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
945 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
946 kbmap->bind("Up", FuncRequest(LFUN_UP));
947 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
949 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
950 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
951 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
952 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
954 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
955 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
956 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
957 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
959 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
960 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
962 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
963 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
965 // kbmap->bindings to enable the use of the numeric keypad
967 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
968 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
969 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
970 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
971 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
972 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
973 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
974 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
975 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
976 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
977 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
978 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
979 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
980 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
981 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
982 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
983 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
984 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
985 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
986 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
987 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
988 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
989 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
990 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
994 void LyX::emergencyCleanup() const
996 // what to do about tmpfiles is non-obvious. we would
997 // like to delete any we find, but our lyxdir might
998 // contain documents etc. which might be helpful on
1001 pimpl_->buffer_list_.emergencyWriteAll();
1003 if (pimpl_->lyx_server_)
1004 pimpl_->lyx_server_->emergencyCleanup();
1005 pimpl_->lyx_server_.reset();
1006 pimpl_->lyx_socket_.reset();
1011 void LyX::deadKeyBindings(kb_keymap * kbmap)
1013 // bindKeyings for transparent handling of deadkeys
1014 // The keysyms are gotten from XFree86 X11R6
1015 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1016 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1017 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1018 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1019 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1020 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1021 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1022 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1023 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1024 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1025 // nothing with this name
1026 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1027 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1028 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1029 // nothing with this name either...
1030 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1031 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1032 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1033 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1039 // return true if file does not exist or is older than configure.py.
1040 bool needsUpdate(string const & file)
1042 // We cannot initialize configure_script directly because the package
1043 // is not initialized yet when static objects are constructed.
1044 static string configure_script;
1045 static bool firstrun = true;
1047 configure_script = FileName(addName(
1048 package().system_support(),
1049 "configure.py")).toFilesystemEncoding();
1053 string const absfile = FileName(addName(
1054 package().user_support(), file)).toFilesystemEncoding();
1055 return (! fs::exists(absfile))
1056 || (fs::last_write_time(configure_script)
1057 > fs::last_write_time(absfile));
1063 bool LyX::queryUserLyXDir(bool explicit_userdir)
1065 // Does user directory exist?
1066 string const user_support =
1067 FileName(package().user_support()).toFilesystemEncoding();
1068 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1069 first_start = false;
1071 return needsUpdate("lyxrc.defaults")
1072 || needsUpdate("textclass.lst")
1073 || needsUpdate("packages.lst");
1076 first_start = !explicit_userdir;
1078 // If the user specified explicitly a directory, ask whether
1079 // to create it. If the user says "no", then exit.
1080 if (explicit_userdir &&
1082 _("Missing user LyX directory"),
1083 bformat(_("You have specified a non-existent user "
1084 "LyX directory, %1$s.\n"
1085 "It is needed to keep your own configuration."),
1086 from_utf8(package().user_support())),
1088 _("&Create directory"),
1090 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1091 earlyExit(EXIT_FAILURE);
1094 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1095 from_utf8(package().user_support())))
1098 if (!createDirectory(package().user_support(), 0755)) {
1099 // Failed, so let's exit.
1100 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1102 earlyExit(EXIT_FAILURE);
1109 bool LyX::readRcFile(string const & name)
1111 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1113 FileName const lyxrc_path = libFileSearch(string(), name);
1114 if (!lyxrc_path.empty()) {
1116 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1118 if (lyxrc.read(lyxrc_path) < 0) {
1119 showFileError(name);
1123 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1129 // Read the ui file `name'
1130 bool LyX::readUIFile(string const & name, bool include)
1140 struct keyword_item uitags[ui_last - 1] = {
1141 { "include", ui_include },
1142 { "menuset", ui_menuset },
1143 { "toolbar", ui_toolbar },
1144 { "toolbars", ui_toolbars }
1147 // Ensure that a file is read only once (prevents include loops)
1148 static std::list<string> uifiles;
1149 std::list<string>::const_iterator it = uifiles.begin();
1150 std::list<string>::const_iterator end = uifiles.end();
1151 it = std::find(it, end, name);
1153 lyxerr[Debug::INIT] << "UI file '" << name
1154 << "' has been read already. "
1155 << "Is this an include loop?"
1160 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1165 ui_path = libFileSearch("ui", name, "inc");
1166 if (ui_path.empty())
1167 ui_path = libFileSearch("ui",
1168 changeExtension(name, "inc"));
1171 ui_path = libFileSearch("ui", name, "ui");
1173 if (ui_path.empty()) {
1174 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1175 showFileError(name);
1179 uifiles.push_back(name);
1181 lyxerr[Debug::INIT] << "Found " << name
1182 << " in " << ui_path << endl;
1183 LyXLex lex(uitags, ui_last - 1);
1184 lex.setFile(ui_path);
1186 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1190 if (lyxerr.debugging(Debug::PARSER))
1191 lex.printTable(lyxerr);
1193 while (lex.isOK()) {
1194 switch (lex.lex()) {
1197 string const file = lex.getString();
1198 if (!readUIFile(file, true))
1203 menubackend.read(lex);
1207 toolbarbackend.read(lex);
1211 toolbarbackend.readToolbars(lex);
1215 if (!rtrim(lex.getString()).empty())
1216 lex.printError("LyX::ReadUIFile: "
1217 "Unknown menu tag: `$$Token'");
1225 // Read the languages file `name'
1226 bool LyX::readLanguagesFile(string const & name)
1228 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1230 FileName const lang_path = libFileSearch(string(), name);
1231 if (lang_path.empty()) {
1232 showFileError(name);
1235 languages.read(lang_path);
1240 // Read the encodings file `name'
1241 bool LyX::readEncodingsFile(string const & name)
1243 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1245 FileName const enc_path = libFileSearch(string(), name);
1246 if (enc_path.empty()) {
1247 showFileError(name);
1250 encodings.read(enc_path);
1259 /// return the the number of arguments consumed
1260 typedef boost::function<int(string const &, string const &)> cmd_helper;
1262 int parse_dbg(string const & arg, string const &)
1265 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1266 Debug::showTags(lyxerr);
1269 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1271 lyxerr.level(Debug::value(arg));
1272 Debug::showLevel(lyxerr, lyxerr.level());
1277 int parse_help(string const &, string const &)
1280 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1281 "Command line switches (case sensitive):\n"
1282 "\t-help summarize LyX usage\n"
1283 "\t-userdir dir set user directory to dir\n"
1284 "\t-sysdir dir set system directory to dir\n"
1285 "\t-geometry WxH+X+Y set geometry of the main window\n"
1286 "\t-dbg feature[,feature]...\n"
1287 " select the features to debug.\n"
1288 " Type `lyx -dbg' to see the list of features\n"
1289 "\t-x [--execute] command\n"
1290 " where command is a lyx command.\n"
1291 "\t-e [--export] fmt\n"
1292 " where fmt is the export format of choice.\n"
1293 "\t-i [--import] fmt file.xxx\n"
1294 " where fmt is the import format of choice\n"
1295 " and file.xxx is the file to be imported.\n"
1296 "\t-version summarize version and build info\n"
1297 "Check the LyX man page for more details.")) << endl;
1302 int parse_version(string const &, string const &)
1304 lyxerr << "LyX " << lyx_version
1305 << " (" << lyx_release_date << ")" << endl;
1306 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1308 lyxerr << lyx_version_info << endl;
1313 int parse_sysdir(string const & arg, string const &)
1316 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1319 cl_system_support = arg;
1323 int parse_userdir(string const & arg, string const &)
1326 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1329 cl_user_support = arg;
1333 int parse_execute(string const & arg, string const &)
1336 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1343 int parse_export(string const & type, string const &)
1346 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1347 "--export switch")) << endl;
1350 batch = "buffer-export " + type;
1355 int parse_import(string const & type, string const & file)
1358 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1359 "--import switch")) << endl;
1363 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1367 batch = "buffer-import " + type + ' ' + file;
1371 int parse_geometry(string const & arg1, string const &)
1374 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1375 // remove also the arg
1378 // don't remove "-geometry"
1387 void LyX::easyParse(int & argc, char * argv[])
1389 std::map<string, cmd_helper> cmdmap;
1391 cmdmap["-dbg"] = parse_dbg;
1392 cmdmap["-help"] = parse_help;
1393 cmdmap["--help"] = parse_help;
1394 cmdmap["-version"] = parse_version;
1395 cmdmap["--version"] = parse_version;
1396 cmdmap["-sysdir"] = parse_sysdir;
1397 cmdmap["-userdir"] = parse_userdir;
1398 cmdmap["-x"] = parse_execute;
1399 cmdmap["--execute"] = parse_execute;
1400 cmdmap["-e"] = parse_export;
1401 cmdmap["--export"] = parse_export;
1402 cmdmap["-i"] = parse_import;
1403 cmdmap["--import"] = parse_import;
1404 cmdmap["-geometry"] = parse_geometry;
1406 for (int i = 1; i < argc; ++i) {
1407 std::map<string, cmd_helper>::const_iterator it
1408 = cmdmap.find(argv[i]);
1410 // don't complain if not found - may be parsed later
1411 if (it == cmdmap.end())
1414 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1415 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1417 int const remove = 1 + it->second(arg, arg2);
1419 // Now, remove used arguments by shifting
1420 // the following ones remove places down.
1423 for (int j = i; j < argc; ++j)
1424 argv[j] = argv[j + remove];
1429 batch_command = batch;
1433 FuncStatus getStatus(FuncRequest const & action)
1435 return LyX::ref().lyxFunc().getStatus(action);
1439 void dispatch(FuncRequest const & action)
1441 LyX::ref().lyxFunc().dispatch(action);
1445 BufferList & theBufferList()
1447 return LyX::ref().bufferList();
1451 LyXFunc & theLyXFunc()
1453 return LyX::ref().lyxFunc();
1457 LyXServer & theLyXServer()
1459 // FIXME: this should not be use_gui dependent
1460 BOOST_ASSERT(use_gui);
1461 return LyX::ref().server();
1465 LyXServerSocket & theLyXServerSocket()
1467 // FIXME: this should not be use_gui dependent
1468 BOOST_ASSERT(use_gui);
1469 return LyX::ref().socket();
1473 kb_keymap & theTopLevelKeymap()
1475 BOOST_ASSERT(use_gui);
1476 return LyX::ref().topLevelKeymap();
1480 Converters & theConverters()
1482 return LyX::ref().converters();
1486 Converters & theSystemConverters()
1488 return LyX::ref().systemConverters();
1492 Movers & theMovers()
1494 return LyX::ref().pimpl_->movers_;
1498 Mover const & getMover(std::string const & fmt)
1500 return LyX::ref().pimpl_->movers_(fmt);
1504 void setMover(std::string const & fmt, std::string const & command)
1506 LyX::ref().pimpl_->movers_.set(fmt, command);
1510 Movers & theSystemMovers()
1512 return LyX::ref().pimpl_->system_movers_;
1516 IconvProcessor & utf8ToUcs4()
1518 return LyX::ref().iconvProcessor();
1522 Messages & getMessages(std::string const & language)
1524 return LyX::ref().getMessages(language);
1528 Messages & getGuiMessages()
1530 return LyX::ref().getGuiMessages();