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/ExceptionMessage.h"
57 #include "support/os.h"
58 #include "support/package.h"
59 #include "support/path.h"
60 #include "support/systemcall.h"
61 #include "support/unicode.h"
63 #include <boost/bind.hpp>
64 #include <boost/filesystem/operations.hpp>
75 using support::addName;
76 using support::addPath;
77 using support::bformat;
78 using support::changeExtension;
79 using support::createDirectory;
80 using support::createLyXTmpDir;
81 using support::destroyDir;
82 using support::FileName;
83 using support::fileSearch;
84 using support::getEnv;
85 using support::i18nLibFileSearch;
86 using support::libFileSearch;
87 using support::package;
88 using support::prependEnvPath;
90 using support::Systemcall;
92 namespace Alert = frontend::Alert;
93 namespace os = support::os;
94 namespace fs = boost::filesystem;
103 #ifndef CXX_GLOBAL_CSTD
110 /// are we using the GUI at all?
112 * We default to true and this is changed to false when the export feature is used.
119 // Filled with the command line arguments "foo" of "-sysdir foo" or
121 string cl_system_support;
122 string cl_user_support;
124 std::string geometryArg;
126 LyX * singleton_ = 0;
128 void showFileError(string const & error)
130 Alert::warning(_("Could not read configuration file"),
131 bformat(_("Error while reading the configuration file\n%1$s.\n"
132 "Please check your installation."), from_utf8(error)));
136 void reconfigureUserLyXDir()
138 string const configure_command = package().configure_command();
140 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
141 support::Path p(package().user_support());
143 one.startscript(Systemcall::Wait, configure_command);
144 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
150 /// The main application class private implementation.
151 struct LyX::Singletons
153 Singletons(): iconv(ucs4_codeset, "UTF-8")
155 // Set the default User Interface language as soon as possible.
156 // The language used will be derived from the environment
158 messages_["GUI"] = Messages();
160 /// our function handler
163 BufferList buffer_list_;
165 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
167 boost::scoped_ptr<LyXServer> lyx_server_;
169 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
171 boost::scoped_ptr<frontend::Application> application_;
172 /// lyx session, containing lastfiles, lastfilepos, and lastopened
173 boost::scoped_ptr<Session> session_;
176 IconvProcessor iconv;
178 /// Files to load at start.
179 vector<FileName> files_to_load_;
181 /// The messages translators.
182 map<string, Messages> messages_;
184 /// The file converters.
185 Converters converters_;
187 // The system converters copy after reading lyxrc.defaults.
188 Converters system_converters_;
194 Movers system_movers_;
198 frontend::Application * theApp()
201 return singleton_->pimpl_->application_.get();
214 BOOST_ASSERT(singleton_);
219 LyX const & LyX::cref()
221 BOOST_ASSERT(singleton_);
230 pimpl_.reset(new Singletons);
235 BufferList & LyX::bufferList()
237 return pimpl_->buffer_list_;
241 BufferList const & LyX::bufferList() const
243 return pimpl_->buffer_list_;
247 Session & LyX::session()
249 BOOST_ASSERT(pimpl_->session_.get());
250 return *pimpl_->session_.get();
254 Session const & LyX::session() const
256 BOOST_ASSERT(pimpl_->session_.get());
257 return *pimpl_->session_.get();
261 LyXFunc & LyX::lyxFunc()
263 return pimpl_->lyxfunc_;
267 LyXFunc const & LyX::lyxFunc() const
269 return pimpl_->lyxfunc_;
273 LyXServer & LyX::server()
275 BOOST_ASSERT(pimpl_->lyx_server_.get());
276 return *pimpl_->lyx_server_.get();
280 LyXServer const & LyX::server() const
282 BOOST_ASSERT(pimpl_->lyx_server_.get());
283 return *pimpl_->lyx_server_.get();
287 LyXServerSocket & LyX::socket()
289 BOOST_ASSERT(pimpl_->lyx_socket_.get());
290 return *pimpl_->lyx_socket_.get();
294 LyXServerSocket const & LyX::socket() const
296 BOOST_ASSERT(pimpl_->lyx_socket_.get());
297 return *pimpl_->lyx_socket_.get();
301 frontend::Application & LyX::application()
303 BOOST_ASSERT(pimpl_->application_.get());
304 return *pimpl_->application_.get();
308 frontend::Application const & LyX::application() const
310 BOOST_ASSERT(pimpl_->application_.get());
311 return *pimpl_->application_.get();
315 kb_keymap & LyX::topLevelKeymap()
317 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
318 return *pimpl_->toplevel_keymap_.get();
322 Converters & LyX::converters()
324 return pimpl_->converters_;
328 Converters & LyX::systemConverters()
330 return pimpl_->system_converters_;
334 IconvProcessor & LyX::iconvProcessor()
336 return pimpl_->iconv;
340 kb_keymap const & LyX::topLevelKeymap() const
342 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
343 return *pimpl_->toplevel_keymap_.get();
347 Messages & LyX::getMessages(std::string const & language)
349 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
351 if (it != pimpl_->messages_.end())
354 std::pair<map<string, Messages>::iterator, bool> result =
355 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
357 BOOST_ASSERT(result.second);
358 return result.first->second;
362 Messages & LyX::getGuiMessages()
364 return pimpl_->messages_["GUI"];
368 void LyX::setGuiLanguage(std::string const & language)
370 pimpl_->messages_["GUI"] = Messages(language);
374 Buffer const * const LyX::updateInset(InsetBase const * inset) const
379 Buffer const * buffer_ptr = 0;
380 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
381 vector<int>::const_iterator it = view_ids.begin();
382 vector<int>::const_iterator const end = view_ids.end();
383 for (; it != end; ++it) {
385 pimpl_->application_->gui().view(*it).updateInset(inset);
393 int LyX::exec(int & argc, char * argv[])
395 // Here we need to parse the command line. At least
396 // we need to parse for "-dbg" and "-help"
397 easyParse(argc, argv);
399 try { support::init_package(to_utf8(from_local8bit(argv[0])),
400 cl_system_support, cl_user_support,
401 support::top_build_dir_is_one_level_up);
402 } catch (support::ExceptionMessage const & message) {
403 if (message.type_ == support::ErrorException) {
404 Alert::error(message.title_, message.details_);
406 } else if (message.type_ == support::WarningException) {
407 Alert::warning(message.title_, message.details_);
412 // FIXME: create a ConsoleApplication
413 int exit_status = init(argc, argv);
421 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
426 BufferList::iterator begin = pimpl_->buffer_list_.begin();
427 BufferList::iterator end = pimpl_->buffer_list_.end();
429 bool final_success = false;
430 for (BufferList::iterator I = begin; I != end; ++I) {
432 bool success = false;
433 buf->dispatch(batch_command, &success);
434 final_success |= success;
437 return !final_success;
440 // Force adding of font path _before_ Application is initialized
441 support::os::addFontResources();
443 // Let the frontend parse and remove all arguments that it knows
444 pimpl_->application_.reset(createApplication(argc, argv));
448 // Parse and remove all known arguments in the LyX singleton
449 // Give an error for all remaining ones.
450 int exit_status = init(argc, argv);
452 // Kill the application object before exiting.
453 pimpl_->application_.reset();
460 /* Create a CoreApplication class that will provide the main event loop
461 * and the socket callback registering. With Qt4, only QtCore
462 * library would be needed.
463 * When this is done, a server_mode could be created and the following two
464 * line would be moved out from here.
466 // Note: socket callback must be registered after init(argc, argv)
467 // such that package().temp_dir() is properly initialized.
468 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
469 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
470 os::internal_path(package().temp_dir() + "/lyxsocket")));
472 // Start the real execution loop.
473 exit_status = pimpl_->application_->exec();
477 // Restore original font resources after Application is destroyed.
478 support::os::restoreFontResources();
484 void LyX::prepareExit()
486 // Set a flag that we do quitting from the program,
487 // so no refreshes are necessary.
490 // close buffers first
491 pimpl_->buffer_list_.closeAll();
493 // do any other cleanup procedures now
494 if (package().temp_dir() != package().system_temp_dir()) {
495 lyxerr[Debug::INFO] << "Deleting tmp dir "
496 << package().temp_dir() << endl;
498 if (!destroyDir(FileName(package().temp_dir()))) {
499 docstring const msg =
500 bformat(_("Unable to remove the temporary directory %1$s"),
501 from_utf8(package().temp_dir()));
502 Alert::warning(_("Unable to remove temporary directory"), msg);
507 if (pimpl_->session_)
508 pimpl_->session_->writeFile();
509 pimpl_->session_.reset();
510 pimpl_->lyx_server_.reset();
511 pimpl_->lyx_socket_.reset();
514 // Kill the application object before exiting. This avoids crashes
515 // when exiting on Linux.
516 if (pimpl_->application_)
517 pimpl_->application_.reset();
521 void LyX::earlyExit(int status)
523 BOOST_ASSERT(pimpl_->application_.get());
524 // LyX::pimpl_::application_ is not initialised at this
525 // point so it's safe to just exit after some cleanup.
531 int LyX::init(int & argc, char * argv[])
533 // check for any spurious extra arguments
534 // other than documents
535 for (int argi = 1; argi < argc ; ++argi) {
536 if (argv[argi][0] == '-') {
538 bformat(_("Wrong command line option `%1$s'. Exiting."),
539 from_utf8(argv[argi]))) << endl;
544 // Initialization of LyX (reads lyxrc and more)
545 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
546 bool success = init();
547 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
551 for (int argi = argc - 1; argi >= 1; --argi) {
552 // get absolute path of file and add ".lyx" to
553 // the filename if necessary
554 pimpl_->files_to_load_.push_back(fileSearch(string(),
555 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
556 "lyx", support::allow_unreadable));
560 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
566 void LyX::loadFiles()
568 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
569 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
571 for (; it != end; ++it) {
575 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
576 if (loadLyXFile(buf, *it)) {
577 ErrorList const & el = buf->errorList("Parse");
579 for_each(el.begin(), el.end(),
580 boost::bind(&LyX::printError, this, _1));
583 pimpl_->buffer_list_.release(buf);
588 void LyX::execBatchCommands()
590 // The advantage of doing this here is that the event loop
591 // is already started. So any need for interaction will be
595 // Execute batch commands if available
596 if (batch_command.empty())
599 lyxerr[Debug::INIT] << "About to handle -x '"
600 << batch_command << '\'' << endl;
602 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
606 void LyX::restoreGuiSession()
608 LyXView * view = newLyXView();
610 // if some files were specified at command-line we assume that the
611 // user wants to edit *these* files and not to restore the session.
612 if (!pimpl_->files_to_load_.empty()) {
613 for_each(pimpl_->files_to_load_.begin(),
614 pimpl_->files_to_load_.end(),
615 bind(&LyXView::loadLyXFile, view, _1, true));
616 // clear this list to save a few bytes of RAM
617 pimpl_->files_to_load_.clear();
618 pimpl_->session_->lastOpened().clear();
622 if (!lyxrc.load_session)
625 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
626 // do not add to the lastfile list since these files are restored from
627 // last session, and should be already there (regular files), or should
628 // not be added at all (help files).
629 for_each(lastopened.begin(), lastopened.end(),
630 bind(&LyXView::loadLyXFile, view, _1, false));
632 // clear this list to save a few bytes of RAM
633 pimpl_->session_->lastOpened().clear();
637 LyXView * LyX::newLyXView()
642 // determine windows size and position, from lyxrc and/or session
644 unsigned int width = 690;
645 unsigned int height = 510;
646 // default icon size, will be overwritten by stored session value
647 unsigned int iconSizeXY = 0;
648 bool maximize = false;
650 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
651 width = lyxrc.geometry_width;
652 height = lyxrc.geometry_height;
654 // if lyxrc returns (0,0), then use session info
656 string val = session().sessionInfo().load("WindowWidth");
658 width = convert<unsigned int>(val);
659 val = session().sessionInfo().load("WindowHeight");
661 height = convert<unsigned int>(val);
662 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
664 val = session().sessionInfo().load("IconSizeXY");
666 iconSizeXY = convert<unsigned int>(val);
669 // if user wants to restore window position
672 if (lyxrc.geometry_xysaved) {
673 string val = session().sessionInfo().load("WindowPosX");
675 posx = convert<int>(val);
676 val = session().sessionInfo().load("WindowPosY");
678 posy = convert<int>(val);
681 if (!geometryArg.empty())
687 // create the main window
688 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
696 The SIGHUP signal does not exist on Windows and does not need to be handled.
698 Windows handles SIGFPE and SIGSEGV signals as expected.
700 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
701 cause a new thread to be spawned. This may well result in unexpected
702 behaviour by the single-threaded LyX.
704 SIGTERM signals will come only from another process actually sending
705 that signal using 'raise' in Windows' POSIX compatability layer. It will
706 not come from the general "terminate process" methods that everyone
707 actually uses (and which can't be trapped). Killing an app 'politely' on
708 Windows involves first sending a WM_CLOSE message, something that is
709 caught already by the Qt frontend.
711 For more information see:
713 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
714 ...signals are mostly useless on Windows for a variety of reasons that are
717 'UNIX Application Migration Guide, Chapter 9'
718 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
720 'How To Terminate an Application "Cleanly" in Win32'
721 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
725 static void error_handler(int err_sig)
727 // Throw away any signals other than the first one received.
728 static sig_atomic_t handling_error = false;
731 handling_error = true;
733 // We have received a signal indicating a fatal error, so
734 // try and save the data ASAP.
735 LyX::cref().emergencyCleanup();
737 // These lyxerr calls may or may not work:
739 // Signals are asynchronous, so the main program may be in a very
740 // fragile state when a signal is processed and thus while a signal
741 // handler function executes.
742 // In general, therefore, we should avoid performing any
743 // I/O operations or calling most library and system functions from
746 // This shouldn't matter here, however, as we've already invoked
751 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
755 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
758 lyxerr << "\nlyx: SIGSEGV signal caught\n"
759 "Sorry, you have found a bug in LyX. "
760 "Please read the bug-reporting instructions "
761 "in Help->Introduction and send us a bug report, "
762 "if necessary. Thanks !\nBye." << endl;
770 // Deinstall the signal handlers
772 signal(SIGHUP, SIG_DFL);
774 signal(SIGINT, SIG_DFL);
775 signal(SIGFPE, SIG_DFL);
776 signal(SIGSEGV, SIG_DFL);
777 signal(SIGTERM, SIG_DFL);
780 if (err_sig == SIGSEGV ||
781 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
783 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
792 void LyX::printError(ErrorItem const & ei)
794 docstring tmp = _("LyX: ") + ei.error + char_type(':')
796 std::cerr << to_utf8(tmp) << std::endl;
800 void LyX::initGuiFont()
802 if (lyxrc.roman_font_name.empty())
803 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
805 if (lyxrc.sans_font_name.empty())
806 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
808 if (lyxrc.typewriter_font_name.empty())
809 lyxrc.typewriter_font_name
810 = pimpl_->application_->typewriterFontName();
817 signal(SIGHUP, error_handler);
819 signal(SIGFPE, error_handler);
820 signal(SIGSEGV, error_handler);
821 signal(SIGINT, error_handler);
822 signal(SIGTERM, error_handler);
823 // SIGPIPE can be safely ignored.
825 lyxrc.tempdir_path = package().temp_dir();
826 lyxrc.document_path = package().document_dir();
828 if (lyxrc.template_path.empty()) {
829 lyxrc.template_path = addPath(package().system_support(),
834 // Read configuration files
837 // This one may have been distributed along with LyX.
838 if (!readRcFile("lyxrc.dist"))
841 // Set the language defined by the distributor.
842 //setGuiLanguage(lyxrc.gui_language);
844 // Set the PATH correctly.
845 #if !defined (USE_POSIX_PACKAGING)
846 // Add the directory containing the LyX executable to the path
847 // so that LyX can find things like tex2lyx.
848 if (package().build_support().empty())
849 prependEnvPath("PATH", package().binary_dir());
851 if (!lyxrc.path_prefix.empty())
852 prependEnvPath("PATH", lyxrc.path_prefix);
854 // Check that user LyX directory is ok.
855 if (queryUserLyXDir(package().explicit_user_support()))
856 reconfigureUserLyXDir();
858 // no need for a splash when there is no GUI
863 // This one is generated in user_support directory by lib/configure.py.
864 if (!readRcFile("lyxrc.defaults"))
867 // Query the OS to know what formats are viewed natively
868 formats.setAutoOpen();
870 // Read lyxrc.dist again to be able to override viewer auto-detection.
871 readRcFile("lyxrc.dist");
873 system_lyxrc = lyxrc;
874 system_formats = formats;
875 pimpl_->system_converters_ = pimpl_->converters_;
876 pimpl_->system_movers_ = pimpl_->movers_;
877 system_lcolor = lcolor;
879 // This one is edited through the preferences dialog.
880 if (!readRcFile("preferences"))
883 if (!readEncodingsFile("encodings", "unicodesymbols"))
885 if (!readLanguagesFile("languages"))
889 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
894 // Set the language defined by the user.
895 //setGuiLanguage(lyxrc.gui_language);
898 pimpl_->toplevel_keymap_.reset(new kb_keymap);
899 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
900 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
902 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
905 if (!readUIFile(lyxrc.ui_file))
909 if (lyxerr.debugging(Debug::LYXRC))
912 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
913 if (!lyxrc.path_prefix.empty())
914 prependEnvPath("PATH", lyxrc.path_prefix);
916 FileName const document_path(lyxrc.document_path);
917 if (fs::exists(document_path.toFilesystemEncoding()) &&
918 fs::is_directory(document_path.toFilesystemEncoding()))
919 package().document_dir() = lyxrc.document_path;
921 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
922 if (package().temp_dir().empty()) {
923 Alert::error(_("Could not create temporary directory"),
924 bformat(_("Could not create a temporary directory in\n"
925 "%1$s. Make sure that this\n"
926 "path exists and is writable and try again."),
927 from_utf8(lyxrc.tempdir_path)));
928 // createLyXTmpDir() tries sufficiently hard to create a
929 // usable temp dir, so the probability to come here is
930 // close to zero. We therefore don't try to overcome this
931 // problem with e.g. asking the user for a new path and
932 // trying again but simply exit.
936 if (lyxerr.debugging(Debug::INIT)) {
937 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
940 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
941 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
943 // This must happen after package initialization and after lyxrc is
944 // read, therefore it can't be done by a static object.
945 ConverterCache::init();
951 void LyX::defaultKeyBindings(kb_keymap * kbmap)
953 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
954 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
955 kbmap->bind("Up", FuncRequest(LFUN_UP));
956 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
958 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
959 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
960 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
961 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
963 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
964 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
965 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
966 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
968 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
969 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
971 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
972 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
974 // kbmap->bindings to enable the use of the numeric keypad
976 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
977 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
978 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
979 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
980 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
981 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
982 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
983 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
984 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
985 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
986 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
987 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
988 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
989 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
990 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
991 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
992 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
993 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
994 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
995 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
996 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
997 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
998 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
999 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1003 void LyX::emergencyCleanup() const
1005 // what to do about tmpfiles is non-obvious. we would
1006 // like to delete any we find, but our lyxdir might
1007 // contain documents etc. which might be helpful on
1010 pimpl_->buffer_list_.emergencyWriteAll();
1012 if (pimpl_->lyx_server_)
1013 pimpl_->lyx_server_->emergencyCleanup();
1014 pimpl_->lyx_server_.reset();
1015 pimpl_->lyx_socket_.reset();
1020 void LyX::deadKeyBindings(kb_keymap * kbmap)
1022 // bindKeyings for transparent handling of deadkeys
1023 // The keysyms are gotten from XFree86 X11R6
1024 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1025 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1026 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1027 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1028 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1029 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1030 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1031 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1032 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1033 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1034 // nothing with this name
1035 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1036 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1037 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1038 // nothing with this name either...
1039 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1040 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1041 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1042 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1048 // return true if file does not exist or is older than configure.py.
1049 bool needsUpdate(string const & file)
1051 // We cannot initialize configure_script directly because the package
1052 // is not initialized yet when static objects are constructed.
1053 static string configure_script;
1054 static bool firstrun = true;
1056 configure_script = FileName(addName(
1057 package().system_support(),
1058 "configure.py")).toFilesystemEncoding();
1062 string const absfile = FileName(addName(
1063 package().user_support(), file)).toFilesystemEncoding();
1064 return (! fs::exists(absfile))
1065 || (fs::last_write_time(configure_script)
1066 > fs::last_write_time(absfile));
1072 bool LyX::queryUserLyXDir(bool explicit_userdir)
1074 // Does user directory exist?
1075 string const user_support =
1076 FileName(package().user_support()).toFilesystemEncoding();
1077 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1078 first_start = false;
1080 return needsUpdate("lyxrc.defaults")
1081 || needsUpdate("textclass.lst")
1082 || needsUpdate("packages.lst");
1085 first_start = !explicit_userdir;
1087 // If the user specified explicitly a directory, ask whether
1088 // to create it. If the user says "no", then exit.
1089 if (explicit_userdir &&
1091 _("Missing user LyX directory"),
1092 bformat(_("You have specified a non-existent user "
1093 "LyX directory, %1$s.\n"
1094 "It is needed to keep your own configuration."),
1095 from_utf8(package().user_support())),
1097 _("&Create directory"),
1099 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1100 earlyExit(EXIT_FAILURE);
1103 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1104 from_utf8(package().user_support())))
1107 if (!createDirectory(package().user_support(), 0755)) {
1108 // Failed, so let's exit.
1109 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1111 earlyExit(EXIT_FAILURE);
1118 bool LyX::readRcFile(string const & name)
1120 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1122 FileName const lyxrc_path = libFileSearch(string(), name);
1123 if (!lyxrc_path.empty()) {
1125 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1127 if (lyxrc.read(lyxrc_path) < 0) {
1128 showFileError(name);
1132 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1138 // Read the ui file `name'
1139 bool LyX::readUIFile(string const & name, bool include)
1149 struct keyword_item uitags[ui_last - 1] = {
1150 { "include", ui_include },
1151 { "menuset", ui_menuset },
1152 { "toolbar", ui_toolbar },
1153 { "toolbars", ui_toolbars }
1156 // Ensure that a file is read only once (prevents include loops)
1157 static std::list<string> uifiles;
1158 std::list<string>::const_iterator it = uifiles.begin();
1159 std::list<string>::const_iterator end = uifiles.end();
1160 it = std::find(it, end, name);
1162 lyxerr[Debug::INIT] << "UI file '" << name
1163 << "' has been read already. "
1164 << "Is this an include loop?"
1169 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1174 ui_path = libFileSearch("ui", name, "inc");
1175 if (ui_path.empty())
1176 ui_path = libFileSearch("ui",
1177 changeExtension(name, "inc"));
1180 ui_path = libFileSearch("ui", name, "ui");
1182 if (ui_path.empty()) {
1183 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1184 showFileError(name);
1188 uifiles.push_back(name);
1190 lyxerr[Debug::INIT] << "Found " << name
1191 << " in " << ui_path << endl;
1192 LyXLex lex(uitags, ui_last - 1);
1193 lex.setFile(ui_path);
1195 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1199 if (lyxerr.debugging(Debug::PARSER))
1200 lex.printTable(lyxerr);
1202 while (lex.isOK()) {
1203 switch (lex.lex()) {
1206 string const file = lex.getString();
1207 if (!readUIFile(file, true))
1212 menubackend.read(lex);
1216 toolbarbackend.read(lex);
1220 toolbarbackend.readToolbars(lex);
1224 if (!rtrim(lex.getString()).empty())
1225 lex.printError("LyX::ReadUIFile: "
1226 "Unknown menu tag: `$$Token'");
1234 // Read the languages file `name'
1235 bool LyX::readLanguagesFile(string const & name)
1237 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1239 FileName const lang_path = libFileSearch(string(), name);
1240 if (lang_path.empty()) {
1241 showFileError(name);
1244 languages.read(lang_path);
1249 // Read the encodings file `name'
1250 bool LyX::readEncodingsFile(string const & enc_name,
1251 string const & symbols_name)
1253 lyxerr[Debug::INIT] << "About to read " << enc_name << " and "
1254 << symbols_name << "..." << endl;
1256 FileName const symbols_path = libFileSearch(string(), symbols_name);
1257 if (symbols_path.empty()) {
1258 showFileError(symbols_name);
1262 FileName const enc_path = libFileSearch(string(), enc_name);
1263 if (enc_path.empty()) {
1264 showFileError(enc_name);
1267 encodings.read(enc_path, symbols_path);
1276 /// return the the number of arguments consumed
1277 typedef boost::function<int(string const &, string const &)> cmd_helper;
1279 int parse_dbg(string const & arg, string const &)
1282 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1283 Debug::showTags(lyxerr);
1286 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1288 lyxerr.level(Debug::value(arg));
1289 Debug::showLevel(lyxerr, lyxerr.level());
1294 int parse_help(string const &, string const &)
1297 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1298 "Command line switches (case sensitive):\n"
1299 "\t-help summarize LyX usage\n"
1300 "\t-userdir dir set user directory to dir\n"
1301 "\t-sysdir dir set system directory to dir\n"
1302 "\t-geometry WxH+X+Y set geometry of the main window\n"
1303 "\t-dbg feature[,feature]...\n"
1304 " select the features to debug.\n"
1305 " Type `lyx -dbg' to see the list of features\n"
1306 "\t-x [--execute] command\n"
1307 " where command is a lyx command.\n"
1308 "\t-e [--export] fmt\n"
1309 " where fmt is the export format of choice.\n"
1310 "\t-i [--import] fmt file.xxx\n"
1311 " where fmt is the import format of choice\n"
1312 " and file.xxx is the file to be imported.\n"
1313 "\t-version summarize version and build info\n"
1314 "Check the LyX man page for more details.")) << endl;
1319 int parse_version(string const &, string const &)
1321 lyxerr << "LyX " << lyx_version
1322 << " (" << lyx_release_date << ")" << endl;
1323 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1325 lyxerr << lyx_version_info << endl;
1330 int parse_sysdir(string const & arg, string const &)
1333 Alert::error(_("No system directory"),
1334 _("Missing directory for -sysdir switch"));
1337 cl_system_support = arg;
1341 int parse_userdir(string const & arg, string const &)
1344 Alert::error(_("No user directory"),
1345 _("Missing directory for -userdir switch"));
1348 cl_user_support = arg;
1352 int parse_execute(string const & arg, string const &)
1355 Alert::error(_("Incomplete command"),
1356 _("Missing command string after --execute switch"));
1363 int parse_export(string const & type, string const &)
1366 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1367 "--export switch")) << endl;
1370 batch = "buffer-export " + type;
1375 int parse_import(string const & type, string const & file)
1378 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1379 "--import switch")) << endl;
1383 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1387 batch = "buffer-import " + type + ' ' + file;
1391 int parse_geometry(string const & arg1, string const &)
1394 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1395 // remove also the arg
1398 // don't remove "-geometry"
1407 void LyX::easyParse(int & argc, char * argv[])
1409 std::map<string, cmd_helper> cmdmap;
1411 cmdmap["-dbg"] = parse_dbg;
1412 cmdmap["-help"] = parse_help;
1413 cmdmap["--help"] = parse_help;
1414 cmdmap["-version"] = parse_version;
1415 cmdmap["--version"] = parse_version;
1416 cmdmap["-sysdir"] = parse_sysdir;
1417 cmdmap["-userdir"] = parse_userdir;
1418 cmdmap["-x"] = parse_execute;
1419 cmdmap["--execute"] = parse_execute;
1420 cmdmap["-e"] = parse_export;
1421 cmdmap["--export"] = parse_export;
1422 cmdmap["-i"] = parse_import;
1423 cmdmap["--import"] = parse_import;
1424 cmdmap["-geometry"] = parse_geometry;
1426 for (int i = 1; i < argc; ++i) {
1427 std::map<string, cmd_helper>::const_iterator it
1428 = cmdmap.find(argv[i]);
1430 // don't complain if not found - may be parsed later
1431 if (it == cmdmap.end())
1434 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1435 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1437 int const remove = 1 + it->second(arg, arg2);
1439 // Now, remove used arguments by shifting
1440 // the following ones remove places down.
1443 for (int j = i; j < argc; ++j)
1444 argv[j] = argv[j + remove];
1449 batch_command = batch;
1453 FuncStatus getStatus(FuncRequest const & action)
1455 return LyX::ref().lyxFunc().getStatus(action);
1459 void dispatch(FuncRequest const & action)
1461 LyX::ref().lyxFunc().dispatch(action);
1465 BufferList & theBufferList()
1467 return LyX::ref().bufferList();
1471 LyXFunc & theLyXFunc()
1473 return LyX::ref().lyxFunc();
1477 LyXServer & theLyXServer()
1479 // FIXME: this should not be use_gui dependent
1480 BOOST_ASSERT(use_gui);
1481 return LyX::ref().server();
1485 LyXServerSocket & theLyXServerSocket()
1487 // FIXME: this should not be use_gui dependent
1488 BOOST_ASSERT(use_gui);
1489 return LyX::ref().socket();
1493 kb_keymap & theTopLevelKeymap()
1495 BOOST_ASSERT(use_gui);
1496 return LyX::ref().topLevelKeymap();
1500 Converters & theConverters()
1502 return LyX::ref().converters();
1506 Converters & theSystemConverters()
1508 return LyX::ref().systemConverters();
1512 Movers & theMovers()
1514 return LyX::ref().pimpl_->movers_;
1518 Mover const & getMover(std::string const & fmt)
1520 return LyX::ref().pimpl_->movers_(fmt);
1524 void setMover(std::string const & fmt, std::string const & command)
1526 LyX::ref().pimpl_->movers_.set(fmt, command);
1530 Movers & theSystemMovers()
1532 return LyX::ref().pimpl_->system_movers_;
1536 IconvProcessor & utf8ToUcs4()
1538 return LyX::ref().iconvProcessor();
1542 Messages & getMessages(std::string const & language)
1544 return LyX::ref().getMessages(language);
1548 Messages & getGuiMessages()
1550 return LyX::ref().getGuiMessages();