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"
40 #include "ServerSocket.h"
41 #include "TextClassList.h"
42 #include "MenuBackend.h"
45 #include "ToolbarBackend.h"
47 #include "frontends/alert.h"
48 #include "frontends/Application.h"
49 #include "frontends/Dialogs.h"
50 #include "frontends/Gui.h"
51 #include "frontends/LyXView.h"
53 #include "support/environment.h"
54 #include "support/filetools.h"
55 #include "support/lyxlib.h"
56 #include "support/convert.h"
57 #include "support/ExceptionMessage.h"
58 #include "support/os.h"
59 #include "support/Package.h"
60 #include "support/Path.h"
61 #include "support/Systemcall.h"
63 #include <boost/bind.hpp>
64 #include <boost/filesystem/operations.hpp>
80 #ifndef CXX_GLOBAL_CSTD
86 namespace fs = boost::filesystem;
90 using support::addName;
91 using support::addPath;
92 using support::bformat;
93 using support::changeExtension;
94 using support::createDirectory;
95 using support::createLyXTmpDir;
96 using support::destroyDir;
97 using support::FileName;
98 using support::fileSearch;
99 using support::getEnv;
100 using support::i18nLibFileSearch;
101 using support::libFileSearch;
102 using support::package;
103 using support::prependEnvPath;
104 using support::rtrim;
105 using support::Systemcall;
106 using frontend::LyXView;
108 namespace Alert = frontend::Alert;
109 namespace os = support::os;
113 /// are we using the GUI at all?
115 * We default to true and this is changed to false when the export feature is used.
122 // Filled with the command line arguments "foo" of "-sysdir foo" or
124 string cl_system_support;
125 string cl_user_support;
127 std::string geometryArg;
129 LyX * singleton_ = 0;
131 void showFileError(string const & error)
133 Alert::warning(_("Could not read configuration file"),
134 bformat(_("Error while reading the configuration file\n%1$s.\n"
135 "Please check your installation."), from_utf8(error)));
139 void reconfigureUserLyXDir()
141 string const configure_command = package().configure_command();
143 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
144 support::Path p(package().user_support());
146 one.startscript(Systemcall::Wait, configure_command);
147 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
153 /// The main application class private implementation.
154 struct LyX::Singletons
158 // Set the default User Interface language as soon as possible.
159 // The language used will be derived from the environment
161 messages_["GUI"] = Messages();
163 /// our function handler
166 BufferList buffer_list_;
168 boost::scoped_ptr<KeyMap> toplevel_keymap_;
170 boost::scoped_ptr<Server> lyx_server_;
172 boost::scoped_ptr<ServerSocket> lyx_socket_;
174 boost::scoped_ptr<frontend::Application> application_;
175 /// lyx session, containing lastfiles, lastfilepos, and lastopened
176 boost::scoped_ptr<Session> session_;
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);
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 Server & LyX::server()
274 BOOST_ASSERT(pimpl_->lyx_server_.get());
275 return *pimpl_->lyx_server_.get();
279 Server const & LyX::server() const
281 BOOST_ASSERT(pimpl_->lyx_server_.get());
282 return *pimpl_->lyx_server_.get();
286 ServerSocket & LyX::socket()
288 BOOST_ASSERT(pimpl_->lyx_socket_.get());
289 return *pimpl_->lyx_socket_.get();
293 ServerSocket 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 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 KeyMap const & LyX::topLevelKeymap() const
335 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
336 return *pimpl_->toplevel_keymap_.get();
340 Messages & LyX::getMessages(std::string const & language)
342 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
344 if (it != pimpl_->messages_.end())
347 std::pair<map<string, Messages>::iterator, bool> result =
348 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
350 BOOST_ASSERT(result.second);
351 return result.first->second;
355 Messages & LyX::getGuiMessages()
357 return pimpl_->messages_["GUI"];
361 void LyX::setGuiLanguage(std::string const & language)
363 pimpl_->messages_["GUI"] = Messages(language);
367 Buffer const * const LyX::updateInset(Inset const * inset) const
369 if (quitting || !inset)
372 Buffer const * buffer_ptr = 0;
373 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
374 vector<int>::const_iterator it = view_ids.begin();
375 vector<int>::const_iterator const end = view_ids.end();
376 for (; it != end; ++it) {
378 pimpl_->application_->gui().view(*it).updateInset(inset);
386 void LyX::hideDialogs(std::string const & name, Inset * inset) const
388 if (quitting || !use_gui)
391 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
392 vector<int>::const_iterator it = view_ids.begin();
393 vector<int>::const_iterator const end = view_ids.end();
394 for (; it != end; ++it)
395 pimpl_->application_->gui().view(*it).getDialogs().
400 int LyX::exec(int & argc, char * argv[])
402 // Here we need to parse the command line. At least
403 // we need to parse for "-dbg" and "-help"
404 easyParse(argc, argv);
406 try { support::init_package(to_utf8(from_local8bit(argv[0])),
407 cl_system_support, cl_user_support,
408 support::top_build_dir_is_one_level_up);
409 } catch (support::ExceptionMessage const & message) {
410 if (message.type_ == support::ErrorException) {
411 Alert::error(message.title_, message.details_);
413 } else if (message.type_ == support::WarningException) {
414 Alert::warning(message.title_, message.details_);
418 // Reinit the messages machinery in case package() knows
419 // something interesting about the locale directory.
423 // FIXME: create a ConsoleApplication
424 int exit_status = init(argc, argv);
432 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
437 BufferList::iterator begin = pimpl_->buffer_list_.begin();
438 BufferList::iterator end = pimpl_->buffer_list_.end();
440 bool final_success = false;
441 for (BufferList::iterator I = begin; I != end; ++I) {
443 bool success = false;
444 buf->dispatch(batch_command, &success);
445 final_success |= success;
448 return !final_success;
451 // Force adding of font path _before_ Application is initialized
452 support::os::addFontResources();
454 // Let the frontend parse and remove all arguments that it knows
455 pimpl_->application_.reset(createApplication(argc, argv));
459 // Parse and remove all known arguments in the LyX singleton
460 // Give an error for all remaining ones.
461 int exit_status = init(argc, argv);
463 // Kill the application object before exiting.
464 pimpl_->application_.reset();
471 /* Create a CoreApplication class that will provide the main event loop
472 * and the socket callback registering. With Qt4, only QtCore
473 * library would be needed.
474 * When this is done, a server_mode could be created and the following two
475 * line would be moved out from here.
477 // Note: socket callback must be registered after init(argc, argv)
478 // such that package().temp_dir() is properly initialized.
479 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
480 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
481 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
483 // Start the real execution loop.
484 exit_status = pimpl_->application_->exec();
488 // Restore original font resources after Application is destroyed.
489 support::os::restoreFontResources();
495 void LyX::prepareExit()
497 // Set a flag that we do quitting from the program,
498 // so no refreshes are necessary.
501 // close buffers first
502 pimpl_->buffer_list_.closeAll();
504 // do any other cleanup procedures now
505 if (package().temp_dir() != package().system_temp_dir()) {
506 LYXERR(Debug::INFO) << "Deleting tmp dir "
507 << package().temp_dir().absFilename() << endl;
509 if (!destroyDir(package().temp_dir())) {
510 docstring const msg =
511 bformat(_("Unable to remove the temporary directory %1$s"),
512 from_utf8(package().temp_dir().absFilename()));
513 Alert::warning(_("Unable to remove temporary directory"), msg);
518 if (pimpl_->session_)
519 pimpl_->session_->writeFile();
520 pimpl_->session_.reset();
521 pimpl_->lyx_server_.reset();
522 pimpl_->lyx_socket_.reset();
525 // Kill the application object before exiting. This avoids crashes
526 // when exiting on Linux.
527 if (pimpl_->application_)
528 pimpl_->application_.reset();
532 void LyX::earlyExit(int status)
534 BOOST_ASSERT(pimpl_->application_.get());
535 // LyX::pimpl_::application_ is not initialised at this
536 // point so it's safe to just exit after some cleanup.
542 int LyX::init(int & argc, char * argv[])
544 // check for any spurious extra arguments
545 // other than documents
546 for (int argi = 1; argi < argc ; ++argi) {
547 if (argv[argi][0] == '-') {
549 bformat(_("Wrong command line option `%1$s'. Exiting."),
550 from_utf8(argv[argi]))) << endl;
555 // Initialization of LyX (reads lyxrc and more)
556 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
557 bool success = init();
558 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
562 for (int argi = argc - 1; argi >= 1; --argi) {
563 // get absolute path of file and add ".lyx" to
564 // the filename if necessary
565 pimpl_->files_to_load_.push_back(fileSearch(string(),
566 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
567 "lyx", support::allow_unreadable));
571 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
577 void LyX::addFileToLoad(FileName const & fname)
579 vector<FileName>::const_iterator cit = std::find(
580 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
583 if (cit == pimpl_->files_to_load_.end())
584 pimpl_->files_to_load_.push_back(fname);
588 void LyX::loadFiles()
590 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
591 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
593 for (; it != end; ++it) {
597 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
598 if (loadLyXFile(buf, *it)) {
599 ErrorList const & el = buf->errorList("Parse");
601 for_each(el.begin(), el.end(),
602 boost::bind(&LyX::printError, this, _1));
605 pimpl_->buffer_list_.release(buf);
610 void LyX::execBatchCommands()
612 // The advantage of doing this here is that the event loop
613 // is already started. So any need for interaction will be
617 // Execute batch commands if available
618 if (batch_command.empty())
621 LYXERR(Debug::INIT) << "About to handle -x '"
622 << batch_command << '\'' << endl;
624 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
628 void LyX::restoreGuiSession()
630 LyXView * view = newLyXView();
632 // if some files were specified at command-line we assume that the
633 // user wants to edit *these* files and not to restore the session.
634 if (!pimpl_->files_to_load_.empty()) {
635 for_each(pimpl_->files_to_load_.begin(),
636 pimpl_->files_to_load_.end(),
637 bind(&LyXView::loadLyXFile, view, _1, true, false, false));
638 // clear this list to save a few bytes of RAM
639 pimpl_->files_to_load_.clear();
640 pimpl_->session_->lastOpened().clear();
644 if (!lyxrc.load_session)
647 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
648 // do not add to the lastfile list since these files are restored from
649 // last session, and should be already there (regular files), or should
650 // not be added at all (help files).
651 for_each(lastopened.begin(), lastopened.end(),
652 bind(&LyXView::loadLyXFile, view, _1, false, false, false));
654 // clear this list to save a few bytes of RAM
655 pimpl_->session_->lastOpened().clear();
659 LyXView * LyX::newLyXView()
664 // determine windows size and position, from lyxrc and/or session
666 unsigned int width = 690;
667 unsigned int height = 510;
668 // default icon size, will be overwritten by stored session value
669 unsigned int iconSizeXY = 0;
670 int maximized = LyXView::NotMaximized;
672 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
673 width = lyxrc.geometry_width;
674 height = lyxrc.geometry_height;
676 // if lyxrc returns (0,0), then use session info
678 string val = session().sessionInfo().load("WindowWidth");
680 width = convert<unsigned int>(val);
681 val = session().sessionInfo().load("WindowHeight");
683 height = convert<unsigned int>(val);
684 val = session().sessionInfo().load("WindowMaximized");
686 maximized = convert<int>(val);
687 val = session().sessionInfo().load("IconSizeXY");
689 iconSizeXY = convert<unsigned int>(val);
692 // if user wants to restore window position
695 if (lyxrc.geometry_xysaved) {
696 string val = session().sessionInfo().load("WindowPosX");
698 posx = convert<int>(val);
699 val = session().sessionInfo().load("WindowPosY");
701 posy = convert<int>(val);
704 if (!geometryArg.empty())
710 // create the main window
711 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
719 The SIGHUP signal does not exist on Windows and does not need to be handled.
721 Windows handles SIGFPE and SIGSEGV signals as expected.
723 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
724 cause a new thread to be spawned. This may well result in unexpected
725 behaviour by the single-threaded LyX.
727 SIGTERM signals will come only from another process actually sending
728 that signal using 'raise' in Windows' POSIX compatability layer. It will
729 not come from the general "terminate process" methods that everyone
730 actually uses (and which can't be trapped). Killing an app 'politely' on
731 Windows involves first sending a WM_CLOSE message, something that is
732 caught already by the Qt frontend.
734 For more information see:
736 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
737 ...signals are mostly useless on Windows for a variety of reasons that are
740 'UNIX Application Migration Guide, Chapter 9'
741 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
743 'How To Terminate an Application "Cleanly" in Win32'
744 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
748 static void error_handler(int err_sig)
750 // Throw away any signals other than the first one received.
751 static sig_atomic_t handling_error = false;
754 handling_error = true;
756 // We have received a signal indicating a fatal error, so
757 // try and save the data ASAP.
758 LyX::cref().emergencyCleanup();
760 // These lyxerr calls may or may not work:
762 // Signals are asynchronous, so the main program may be in a very
763 // fragile state when a signal is processed and thus while a signal
764 // handler function executes.
765 // In general, therefore, we should avoid performing any
766 // I/O operations or calling most library and system functions from
769 // This shouldn't matter here, however, as we've already invoked
774 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
778 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
781 lyxerr << "\nlyx: SIGSEGV signal caught\n"
782 "Sorry, you have found a bug in LyX. "
783 "Please read the bug-reporting instructions "
784 "in Help->Introduction and send us a bug report, "
785 "if necessary. Thanks !\nBye." << endl;
793 // Deinstall the signal handlers
795 signal(SIGHUP, SIG_DFL);
797 signal(SIGINT, SIG_DFL);
798 signal(SIGFPE, SIG_DFL);
799 signal(SIGSEGV, SIG_DFL);
800 signal(SIGTERM, SIG_DFL);
803 if (err_sig == SIGSEGV ||
804 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
806 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
815 void LyX::printError(ErrorItem const & ei)
817 docstring tmp = _("LyX: ") + ei.error + char_type(':')
819 std::cerr << to_utf8(tmp) << std::endl;
823 void LyX::initGuiFont()
825 if (lyxrc.roman_font_name.empty())
826 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
828 if (lyxrc.sans_font_name.empty())
829 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
831 if (lyxrc.typewriter_font_name.empty())
832 lyxrc.typewriter_font_name
833 = pimpl_->application_->typewriterFontName();
840 signal(SIGHUP, error_handler);
842 signal(SIGFPE, error_handler);
843 signal(SIGSEGV, error_handler);
844 signal(SIGINT, error_handler);
845 signal(SIGTERM, error_handler);
846 // SIGPIPE can be safely ignored.
848 lyxrc.tempdir_path = package().temp_dir().absFilename();
849 lyxrc.document_path = package().document_dir().absFilename();
851 if (lyxrc.template_path.empty()) {
852 lyxrc.template_path = addPath(package().system_support().absFilename(),
857 // Read configuration files
860 // This one may have been distributed along with LyX.
861 if (!readRcFile("lyxrc.dist"))
864 // Set the language defined by the distributor.
865 //setGuiLanguage(lyxrc.gui_language);
867 // Set the PATH correctly.
868 #if !defined (USE_POSIX_PACKAGING)
869 // Add the directory containing the LyX executable to the path
870 // so that LyX can find things like tex2lyx.
871 if (package().build_support().empty())
872 prependEnvPath("PATH", package().binary_dir().absFilename());
874 if (!lyxrc.path_prefix.empty())
875 prependEnvPath("PATH", lyxrc.path_prefix);
877 // Check that user LyX directory is ok.
878 if (queryUserLyXDir(package().explicit_user_support()))
879 reconfigureUserLyXDir();
881 // no need for a splash when there is no GUI
886 // This one is generated in user_support directory by lib/configure.py.
887 if (!readRcFile("lyxrc.defaults"))
890 // Query the OS to know what formats are viewed natively
891 formats.setAutoOpen();
893 // Read lyxrc.dist again to be able to override viewer auto-detection.
894 readRcFile("lyxrc.dist");
896 system_lyxrc = lyxrc;
897 system_formats = formats;
898 pimpl_->system_converters_ = pimpl_->converters_;
899 pimpl_->system_movers_ = pimpl_->movers_;
900 system_lcolor = lcolor;
902 // This one is edited through the preferences dialog.
903 if (!readRcFile("preferences"))
906 if (!readEncodingsFile("encodings", "unicodesymbols"))
908 if (!readLanguagesFile("languages"))
912 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
917 // Set the language defined by the user.
918 //setGuiLanguage(lyxrc.gui_language);
921 pimpl_->toplevel_keymap_.reset(new KeyMap);
922 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
923 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
925 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
928 if (!readUIFile(lyxrc.ui_file))
932 if (lyxerr.debugging(Debug::LYXRC))
935 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
936 if (!lyxrc.path_prefix.empty())
937 prependEnvPath("PATH", lyxrc.path_prefix);
939 FileName const document_path(lyxrc.document_path);
940 if (fs::exists(document_path.toFilesystemEncoding()) &&
941 fs::is_directory(document_path.toFilesystemEncoding()))
942 package().document_dir() = document_path;
944 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
945 if (package().temp_dir().empty()) {
946 Alert::error(_("Could not create temporary directory"),
947 bformat(_("Could not create a temporary directory in\n"
948 "%1$s. Make sure that this\n"
949 "path exists and is writable and try again."),
950 from_utf8(lyxrc.tempdir_path)));
951 // createLyXTmpDir() tries sufficiently hard to create a
952 // usable temp dir, so the probability to come here is
953 // close to zero. We therefore don't try to overcome this
954 // problem with e.g. asking the user for a new path and
955 // trying again but simply exit.
959 LYXERR(Debug::INIT) << "LyX tmp dir: `"
960 << package().temp_dir().absFilename()
963 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
964 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
966 // This must happen after package initialization and after lyxrc is
967 // read, therefore it can't be done by a static object.
968 ConverterCache::init();
974 void LyX::defaultKeyBindings(KeyMap * kbmap)
976 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
977 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
978 kbmap->bind("Up", FuncRequest(LFUN_UP));
979 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
981 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
982 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
983 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
984 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
986 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
987 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
988 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
989 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
991 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
992 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
994 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
995 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
997 // kbmap->bindings to enable the use of the numeric keypad
999 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1000 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1001 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1002 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1003 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1004 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1005 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1006 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1007 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1008 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1009 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1010 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1011 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1012 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1013 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1014 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1015 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1016 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1017 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1018 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1019 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1020 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1021 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1022 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1026 void LyX::emergencyCleanup() const
1028 // what to do about tmpfiles is non-obvious. we would
1029 // like to delete any we find, but our lyxdir might
1030 // contain documents etc. which might be helpful on
1033 pimpl_->buffer_list_.emergencyWriteAll();
1035 if (pimpl_->lyx_server_)
1036 pimpl_->lyx_server_->emergencyCleanup();
1037 pimpl_->lyx_server_.reset();
1038 pimpl_->lyx_socket_.reset();
1043 void LyX::deadKeyBindings(KeyMap * kbmap)
1045 // bindKeyings for transparent handling of deadkeys
1046 // The keysyms are gotten from XFree86 X11R6
1047 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1048 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1049 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1050 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1051 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1052 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1053 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1054 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1055 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1056 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1057 // nothing with this name
1058 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1059 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1060 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1061 // nothing with this name either...
1062 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1063 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1064 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1065 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1071 // return true if file does not exist or is older than configure.py.
1072 bool needsUpdate(string const & file)
1074 // We cannot initialize configure_script directly because the package
1075 // is not initialized yet when static objects are constructed.
1076 static string configure_script;
1077 static bool firstrun = true;
1079 configure_script = FileName(addName(
1080 package().system_support().absFilename(),
1081 "configure.py")).toFilesystemEncoding();
1085 string const absfile = FileName(addName(
1086 package().user_support().absFilename(), file)).toFilesystemEncoding();
1087 return (! fs::exists(absfile))
1088 || (fs::last_write_time(configure_script)
1089 > fs::last_write_time(absfile));
1095 bool LyX::queryUserLyXDir(bool explicit_userdir)
1097 // Does user directory exist?
1098 string const user_support =
1099 package().user_support().toFilesystemEncoding();
1100 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1101 first_start = false;
1103 return needsUpdate("lyxrc.defaults")
1104 || needsUpdate("textclass.lst")
1105 || needsUpdate("packages.lst");
1108 first_start = !explicit_userdir;
1110 // If the user specified explicitly a directory, ask whether
1111 // to create it. If the user says "no", then exit.
1112 if (explicit_userdir &&
1114 _("Missing user LyX directory"),
1115 bformat(_("You have specified a non-existent user "
1116 "LyX directory, %1$s.\n"
1117 "It is needed to keep your own configuration."),
1118 from_utf8(package().user_support().absFilename())),
1120 _("&Create directory"),
1122 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1123 earlyExit(EXIT_FAILURE);
1126 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1127 from_utf8(package().user_support().absFilename())))
1130 if (!createDirectory(package().user_support(), 0755)) {
1131 // Failed, so let's exit.
1132 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1134 earlyExit(EXIT_FAILURE);
1141 bool LyX::readRcFile(string const & name)
1143 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1145 FileName const lyxrc_path = libFileSearch(string(), name);
1146 if (!lyxrc_path.empty()) {
1148 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1150 if (lyxrc.read(lyxrc_path) < 0) {
1151 showFileError(name);
1155 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1161 // Read the ui file `name'
1162 bool LyX::readUIFile(string const & name, bool include)
1172 struct keyword_item uitags[ui_last - 1] = {
1173 { "include", ui_include },
1174 { "menuset", ui_menuset },
1175 { "toolbars", ui_toolbars },
1176 { "toolbarset", ui_toolbarset }
1179 // Ensure that a file is read only once (prevents include loops)
1180 static std::list<string> uifiles;
1181 std::list<string>::const_iterator it = uifiles.begin();
1182 std::list<string>::const_iterator end = uifiles.end();
1183 it = std::find(it, end, name);
1185 LYXERR(Debug::INIT) << "UI file '" << name
1186 << "' has been read already. "
1187 << "Is this an include loop?"
1192 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1197 ui_path = libFileSearch("ui", name, "inc");
1198 if (ui_path.empty())
1199 ui_path = libFileSearch("ui",
1200 changeExtension(name, "inc"));
1203 ui_path = libFileSearch("ui", name, "ui");
1205 if (ui_path.empty()) {
1206 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1207 showFileError(name);
1211 uifiles.push_back(name);
1213 LYXERR(Debug::INIT) << "Found " << name
1214 << " in " << ui_path << endl;
1215 Lexer lex(uitags, ui_last - 1);
1216 lex.setFile(ui_path);
1218 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1222 if (lyxerr.debugging(Debug::PARSER))
1223 lex.printTable(lyxerr);
1225 while (lex.isOK()) {
1226 switch (lex.lex()) {
1229 string const file = lex.getString();
1230 if (!readUIFile(file, true))
1235 menubackend.read(lex);
1239 toolbarbackend.readToolbars(lex);
1243 toolbarbackend.readToolbarSettings(lex);
1247 if (!rtrim(lex.getString()).empty())
1248 lex.printError("LyX::ReadUIFile: "
1249 "Unknown menu tag: `$$Token'");
1257 // Read the languages file `name'
1258 bool LyX::readLanguagesFile(string const & name)
1260 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1262 FileName const lang_path = libFileSearch(string(), name);
1263 if (lang_path.empty()) {
1264 showFileError(name);
1267 languages.read(lang_path);
1272 // Read the encodings file `name'
1273 bool LyX::readEncodingsFile(string const & enc_name,
1274 string const & symbols_name)
1276 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1277 << symbols_name << "..." << endl;
1279 FileName const symbols_path = libFileSearch(string(), symbols_name);
1280 if (symbols_path.empty()) {
1281 showFileError(symbols_name);
1285 FileName const enc_path = libFileSearch(string(), enc_name);
1286 if (enc_path.empty()) {
1287 showFileError(enc_name);
1290 encodings.read(enc_path, symbols_path);
1299 /// return the the number of arguments consumed
1300 typedef boost::function<int(string const &, string const &)> cmd_helper;
1302 int parse_dbg(string const & arg, string const &)
1305 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1306 Debug::showTags(lyxerr);
1309 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1311 lyxerr.level(Debug::value(arg));
1312 Debug::showLevel(lyxerr, lyxerr.level());
1317 int parse_help(string const &, string const &)
1320 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1321 "Command line switches (case sensitive):\n"
1322 "\t-help summarize LyX usage\n"
1323 "\t-userdir dir set user directory to dir\n"
1324 "\t-sysdir dir set system directory to dir\n"
1325 "\t-geometry WxH+X+Y set geometry of the main window\n"
1326 "\t-dbg feature[,feature]...\n"
1327 " select the features to debug.\n"
1328 " Type `lyx -dbg' to see the list of features\n"
1329 "\t-x [--execute] command\n"
1330 " where command is a lyx command.\n"
1331 "\t-e [--export] fmt\n"
1332 " where fmt is the export format of choice.\n"
1333 "\t-i [--import] fmt file.xxx\n"
1334 " where fmt is the import format of choice\n"
1335 " and file.xxx is the file to be imported.\n"
1336 "\t-version summarize version and build info\n"
1337 "Check the LyX man page for more details.")) << endl;
1342 int parse_version(string const &, string const &)
1344 lyxerr << "LyX " << lyx_version
1345 << " (" << lyx_release_date << ")" << endl;
1346 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1348 lyxerr << lyx_version_info << endl;
1353 int parse_sysdir(string const & arg, string const &)
1356 Alert::error(_("No system directory"),
1357 _("Missing directory for -sysdir switch"));
1360 cl_system_support = arg;
1364 int parse_userdir(string const & arg, string const &)
1367 Alert::error(_("No user directory"),
1368 _("Missing directory for -userdir switch"));
1371 cl_user_support = arg;
1375 int parse_execute(string const & arg, string const &)
1378 Alert::error(_("Incomplete command"),
1379 _("Missing command string after --execute switch"));
1386 int parse_export(string const & type, string const &)
1389 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1390 "--export switch")) << endl;
1393 batch = "buffer-export " + type;
1398 int parse_import(string const & type, string const & file)
1401 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1402 "--import switch")) << endl;
1406 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1410 batch = "buffer-import " + type + ' ' + file;
1414 int parse_geometry(string const & arg1, string const &)
1417 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1418 // remove also the arg
1421 // don't remove "-geometry"
1430 void LyX::easyParse(int & argc, char * argv[])
1432 std::map<string, cmd_helper> cmdmap;
1434 cmdmap["-dbg"] = parse_dbg;
1435 cmdmap["-help"] = parse_help;
1436 cmdmap["--help"] = parse_help;
1437 cmdmap["-version"] = parse_version;
1438 cmdmap["--version"] = parse_version;
1439 cmdmap["-sysdir"] = parse_sysdir;
1440 cmdmap["-userdir"] = parse_userdir;
1441 cmdmap["-x"] = parse_execute;
1442 cmdmap["--execute"] = parse_execute;
1443 cmdmap["-e"] = parse_export;
1444 cmdmap["--export"] = parse_export;
1445 cmdmap["-i"] = parse_import;
1446 cmdmap["--import"] = parse_import;
1447 cmdmap["-geometry"] = parse_geometry;
1449 for (int i = 1; i < argc; ++i) {
1450 std::map<string, cmd_helper>::const_iterator it
1451 = cmdmap.find(argv[i]);
1453 // don't complain if not found - may be parsed later
1454 if (it == cmdmap.end())
1457 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1458 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1460 int const remove = 1 + it->second(arg, arg2);
1462 // Now, remove used arguments by shifting
1463 // the following ones remove places down.
1466 for (int j = i; j < argc; ++j)
1467 argv[j] = argv[j + remove];
1472 batch_command = batch;
1476 FuncStatus getStatus(FuncRequest const & action)
1478 return LyX::ref().lyxFunc().getStatus(action);
1482 void dispatch(FuncRequest const & action)
1484 LyX::ref().lyxFunc().dispatch(action);
1488 BufferList & theBufferList()
1490 return LyX::ref().bufferList();
1494 LyXFunc & theLyXFunc()
1496 return LyX::ref().lyxFunc();
1500 Server & theServer()
1502 // FIXME: this should not be use_gui dependent
1503 BOOST_ASSERT(use_gui);
1504 return LyX::ref().server();
1508 ServerSocket & theServerSocket()
1510 // FIXME: this should not be use_gui dependent
1511 BOOST_ASSERT(use_gui);
1512 return LyX::ref().socket();
1516 KeyMap & theTopLevelKeymap()
1518 BOOST_ASSERT(use_gui);
1519 return LyX::ref().topLevelKeymap();
1523 Converters & theConverters()
1525 return LyX::ref().converters();
1529 Converters & theSystemConverters()
1531 return LyX::ref().systemConverters();
1535 Movers & theMovers()
1537 return LyX::ref().pimpl_->movers_;
1541 Mover const & getMover(std::string const & fmt)
1543 return LyX::ref().pimpl_->movers_(fmt);
1547 void setMover(std::string const & fmt, std::string const & command)
1549 LyX::ref().pimpl_->movers_.set(fmt, command);
1553 Movers & theSystemMovers()
1555 return LyX::ref().pimpl_->system_movers_;
1559 Messages & getMessages(std::string const & language)
1561 return LyX::ref().getMessages(language);
1565 Messages & getGuiMessages()
1567 return LyX::ref().getGuiMessages();