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 "ModuleList.h"
41 #include "ServerSocket.h"
42 #include "TextClassList.h"
43 #include "MenuBackend.h"
46 #include "ToolbarBackend.h"
48 #include "frontends/alert.h"
49 #include "frontends/Application.h"
50 #include "frontends/Dialogs.h"
51 #include "frontends/Gui.h"
52 #include "frontends/LyXView.h"
54 #include "support/environment.h"
55 #include "support/filetools.h"
56 #include "support/lyxlib.h"
57 #include "support/convert.h"
58 #include "support/ExceptionMessage.h"
59 #include "support/os.h"
60 #include "support/Package.h"
61 #include "support/Path.h"
62 #include "support/Systemcall.h"
64 #include <boost/bind.hpp>
65 #include <boost/filesystem/operations.hpp>
81 #ifndef CXX_GLOBAL_CSTD
87 namespace fs = boost::filesystem;
91 using support::addName;
92 using support::addPath;
93 using support::bformat;
94 using support::changeExtension;
95 using support::createDirectory;
96 using support::createLyXTmpDir;
97 using support::destroyDir;
98 using support::FileName;
99 using support::fileSearch;
100 using support::getEnv;
101 using support::i18nLibFileSearch;
102 using support::libFileSearch;
103 using support::package;
104 using support::prependEnvPath;
105 using support::rtrim;
106 using support::Systemcall;
107 using frontend::LyXView;
109 namespace Alert = frontend::Alert;
110 namespace os = support::os;
114 /// are we using the GUI at all?
116 * We default to true and this is changed to false when the export feature is used.
123 // Filled with the command line arguments "foo" of "-sysdir foo" or
125 string cl_system_support;
126 string cl_user_support;
128 std::string geometryArg;
130 LyX * singleton_ = 0;
132 void showFileError(string const & error)
134 Alert::warning(_("Could not read configuration file"),
135 bformat(_("Error while reading the configuration file\n%1$s.\n"
136 "Please check your installation."), from_utf8(error)));
140 void reconfigureUserLyXDir()
142 string const configure_command = package().configure_command();
144 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
145 support::Path p(package().user_support());
147 one.startscript(Systemcall::Wait, configure_command);
148 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
154 /// The main application class private implementation.
155 struct LyX::Singletons
159 // Set the default User Interface language as soon as possible.
160 // The language used will be derived from the environment
162 messages_["GUI"] = Messages();
164 /// our function handler
167 BufferList buffer_list_;
169 boost::scoped_ptr<KeyMap> toplevel_keymap_;
171 boost::scoped_ptr<Server> lyx_server_;
173 boost::scoped_ptr<ServerSocket> lyx_socket_;
175 boost::scoped_ptr<frontend::Application> application_;
176 /// lyx session, containing lastfiles, lastfilepos, and lastopened
177 boost::scoped_ptr<Session> session_;
179 /// Files to load at start.
180 vector<FileName> files_to_load_;
182 /// The messages translators.
183 map<string, Messages> messages_;
185 /// The file converters.
186 Converters converters_;
188 // The system converters copy after reading lyxrc.defaults.
189 Converters system_converters_;
195 Movers system_movers_;
199 frontend::Application * theApp()
202 return singleton_->pimpl_->application_.get();
215 BOOST_ASSERT(singleton_);
220 LyX const & LyX::cref()
222 BOOST_ASSERT(singleton_);
231 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 Server & LyX::server()
275 BOOST_ASSERT(pimpl_->lyx_server_.get());
276 return *pimpl_->lyx_server_.get();
280 Server const & LyX::server() const
282 BOOST_ASSERT(pimpl_->lyx_server_.get());
283 return *pimpl_->lyx_server_.get();
287 ServerSocket & LyX::socket()
289 BOOST_ASSERT(pimpl_->lyx_socket_.get());
290 return *pimpl_->lyx_socket_.get();
294 ServerSocket 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 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 KeyMap const & LyX::topLevelKeymap() const
336 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
337 return *pimpl_->toplevel_keymap_.get();
341 Messages & LyX::getMessages(std::string const & language)
343 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
345 if (it != pimpl_->messages_.end())
348 std::pair<map<string, Messages>::iterator, bool> result =
349 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
351 BOOST_ASSERT(result.second);
352 return result.first->second;
356 Messages & LyX::getGuiMessages()
358 return pimpl_->messages_["GUI"];
362 void LyX::setGuiLanguage(std::string const & language)
364 pimpl_->messages_["GUI"] = Messages(language);
368 Buffer const * const LyX::updateInset(Inset const * inset) const
370 if (quitting || !inset)
373 Buffer const * buffer_ptr = 0;
374 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
375 vector<int>::const_iterator it = view_ids.begin();
376 vector<int>::const_iterator const end = view_ids.end();
377 for (; it != end; ++it) {
379 pimpl_->application_->gui().view(*it).updateInset(inset);
387 void LyX::hideDialogs(std::string const & name, Inset * inset) const
389 if (quitting || !use_gui)
392 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
393 vector<int>::const_iterator it = view_ids.begin();
394 vector<int>::const_iterator const end = view_ids.end();
395 for (; it != end; ++it)
396 pimpl_->application_->gui().view(*it).getDialogs().
401 int LyX::exec(int & argc, char * argv[])
403 // Here we need to parse the command line. At least
404 // we need to parse for "-dbg" and "-help"
405 easyParse(argc, argv);
407 try { support::init_package(to_utf8(from_local8bit(argv[0])),
408 cl_system_support, cl_user_support,
409 support::top_build_dir_is_one_level_up);
410 } catch (support::ExceptionMessage const & message) {
411 if (message.type_ == support::ErrorException) {
412 Alert::error(message.title_, message.details_);
414 } else if (message.type_ == support::WarningException) {
415 Alert::warning(message.title_, message.details_);
419 // Reinit the messages machinery in case package() knows
420 // something interesting about the locale directory.
424 // FIXME: create a ConsoleApplication
425 int exit_status = init(argc, argv);
433 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
438 BufferList::iterator begin = pimpl_->buffer_list_.begin();
440 bool final_success = false;
441 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
443 if (buf != buf->getMasterBuffer())
445 bool success = false;
446 buf->dispatch(batch_command, &success);
447 final_success |= success;
450 return !final_success;
453 // Let the frontend parse and remove all arguments that it knows
454 pimpl_->application_.reset(createApplication(argc, argv));
458 // Parse and remove all known arguments in the LyX singleton
459 // Give an error for all remaining ones.
460 int exit_status = init(argc, argv);
462 // Kill the application object before exiting.
463 pimpl_->application_.reset();
470 /* Create a CoreApplication class that will provide the main event loop
471 * and the socket callback registering. With Qt4, only QtCore
472 * library would be needed.
473 * When this is done, a server_mode could be created and the following two
474 * line would be moved out from here.
476 // Note: socket callback must be registered after init(argc, argv)
477 // such that package().temp_dir() is properly initialized.
478 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
479 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
480 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
482 // Start the real execution loop.
483 exit_status = pimpl_->application_->exec();
491 void LyX::prepareExit()
493 // Set a flag that we do quitting from the program,
494 // so no refreshes are necessary.
497 // close buffers first
498 pimpl_->buffer_list_.closeAll();
500 // do any other cleanup procedures now
501 if (package().temp_dir() != package().system_temp_dir()) {
502 LYXERR(Debug::INFO) << "Deleting tmp dir "
503 << package().temp_dir().absFilename() << endl;
505 if (!destroyDir(package().temp_dir())) {
506 docstring const msg =
507 bformat(_("Unable to remove the temporary directory %1$s"),
508 from_utf8(package().temp_dir().absFilename()));
509 Alert::warning(_("Unable to remove temporary directory"), msg);
514 if (pimpl_->session_)
515 pimpl_->session_->writeFile();
516 pimpl_->session_.reset();
517 pimpl_->lyx_server_.reset();
518 pimpl_->lyx_socket_.reset();
521 // Kill the application object before exiting. This avoids crashes
522 // when exiting on Linux.
523 if (pimpl_->application_)
524 pimpl_->application_.reset();
528 void LyX::earlyExit(int status)
530 BOOST_ASSERT(pimpl_->application_.get());
531 // LyX::pimpl_::application_ is not initialised at this
532 // point so it's safe to just exit after some cleanup.
538 int LyX::init(int & argc, char * argv[])
540 // check for any spurious extra arguments
541 // other than documents
542 for (int argi = 1; argi < argc ; ++argi) {
543 if (argv[argi][0] == '-') {
545 bformat(_("Wrong command line option `%1$s'. Exiting."),
546 from_utf8(argv[argi]))) << endl;
551 // Initialization of LyX (reads lyxrc and more)
552 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
553 bool success = init();
554 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
558 for (int argi = argc - 1; argi >= 1; --argi) {
559 // get absolute path of file and add ".lyx" to
560 // the filename if necessary
561 pimpl_->files_to_load_.push_back(fileSearch(string(),
562 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
563 "lyx", support::allow_unreadable));
567 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
573 void LyX::addFileToLoad(FileName const & fname)
575 vector<FileName>::const_iterator cit = std::find(
576 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
579 if (cit == pimpl_->files_to_load_.end())
580 pimpl_->files_to_load_.push_back(fname);
584 void LyX::loadFiles()
586 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
587 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
589 for (; it != end; ++it) {
593 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
594 if (loadLyXFile(buf, *it)) {
595 ErrorList const & el = buf->errorList("Parse");
597 for_each(el.begin(), el.end(),
598 boost::bind(&LyX::printError, this, _1));
601 pimpl_->buffer_list_.release(buf);
606 void LyX::execBatchCommands()
608 // The advantage of doing this here is that the event loop
609 // is already started. So any need for interaction will be
613 // Execute batch commands if available
614 if (batch_command.empty())
617 LYXERR(Debug::INIT) << "About to handle -x '"
618 << batch_command << '\'' << endl;
620 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
624 void LyX::restoreGuiSession()
626 LyXView * view = newLyXView();
628 // if some files were specified at command-line we assume that the
629 // user wants to edit *these* files and not to restore the session.
630 if (!pimpl_->files_to_load_.empty()) {
631 for_each(pimpl_->files_to_load_.begin(),
632 pimpl_->files_to_load_.end(),
633 bind(&LyXView::loadLyXFile, view, _1, true));
634 // clear this list to save a few bytes of RAM
635 pimpl_->files_to_load_.clear();
636 pimpl_->session_->lastOpened().clear();
638 } else if (lyxrc.load_session) {
639 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
640 // do not add to the lastfile list since these files are restored from
641 // last session, and should be already there (regular files), or should
642 // not be added at all (help files).
643 for_each(lastopened.begin(), lastopened.end(),
644 bind(&LyXView::loadLyXFile, view, _1, false));
646 // clear this list to save a few bytes of RAM
647 pimpl_->session_->lastOpened().clear();
650 BufferList::iterator I = pimpl_->buffer_list_.begin();
651 BufferList::iterator end = pimpl_->buffer_list_.end();
652 for (; I != end; ++I) {
654 if (buf != buf->getMasterBuffer())
659 // FIXME: Switch to the last loaded Buffer. This must not be the first one
660 // because the Buffer won't be connected in this case. The correct solution
661 // would be to avoid the manual connection of the current Buffer in LyXView.
662 if (!pimpl_->buffer_list_.empty())
663 view->setBuffer(pimpl_->buffer_list_.last());
667 LyXView * LyX::newLyXView()
672 // determine windows size and position, from lyxrc and/or session
674 unsigned int width = 690;
675 unsigned int height = 510;
676 // default icon size, will be overwritten by stored session value
677 unsigned int iconSizeXY = 0;
678 int maximized = LyXView::NotMaximized;
680 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
681 width = lyxrc.geometry_width;
682 height = lyxrc.geometry_height;
684 // if lyxrc returns (0,0), then use session info
686 string val = session().sessionInfo().load("WindowWidth");
688 width = convert<unsigned int>(val);
689 val = session().sessionInfo().load("WindowHeight");
691 height = convert<unsigned int>(val);
692 val = session().sessionInfo().load("WindowMaximized");
694 maximized = convert<int>(val);
695 val = session().sessionInfo().load("IconSizeXY");
697 iconSizeXY = convert<unsigned int>(val);
700 // if user wants to restore window position
703 if (lyxrc.geometry_xysaved) {
704 string val = session().sessionInfo().load("WindowPosX");
706 posx = convert<int>(val);
707 val = session().sessionInfo().load("WindowPosY");
709 posy = convert<int>(val);
712 if (!geometryArg.empty())
718 // create the main window
719 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
727 The SIGHUP signal does not exist on Windows and does not need to be handled.
729 Windows handles SIGFPE and SIGSEGV signals as expected.
731 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
732 cause a new thread to be spawned. This may well result in unexpected
733 behaviour by the single-threaded LyX.
735 SIGTERM signals will come only from another process actually sending
736 that signal using 'raise' in Windows' POSIX compatability layer. It will
737 not come from the general "terminate process" methods that everyone
738 actually uses (and which can't be trapped). Killing an app 'politely' on
739 Windows involves first sending a WM_CLOSE message, something that is
740 caught already by the Qt frontend.
742 For more information see:
744 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
745 ...signals are mostly useless on Windows for a variety of reasons that are
748 'UNIX Application Migration Guide, Chapter 9'
749 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
751 'How To Terminate an Application "Cleanly" in Win32'
752 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
756 static void error_handler(int err_sig)
758 // Throw away any signals other than the first one received.
759 static sig_atomic_t handling_error = false;
762 handling_error = true;
764 // We have received a signal indicating a fatal error, so
765 // try and save the data ASAP.
766 LyX::cref().emergencyCleanup();
768 // These lyxerr calls may or may not work:
770 // Signals are asynchronous, so the main program may be in a very
771 // fragile state when a signal is processed and thus while a signal
772 // handler function executes.
773 // In general, therefore, we should avoid performing any
774 // I/O operations or calling most library and system functions from
777 // This shouldn't matter here, however, as we've already invoked
782 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
786 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
789 lyxerr << "\nlyx: SIGSEGV signal caught\n"
790 "Sorry, you have found a bug in LyX. "
791 "Please read the bug-reporting instructions "
792 "in Help->Introduction and send us a bug report, "
793 "if necessary. Thanks !\nBye." << endl;
801 // Deinstall the signal handlers
803 signal(SIGHUP, SIG_DFL);
805 signal(SIGINT, SIG_DFL);
806 signal(SIGFPE, SIG_DFL);
807 signal(SIGSEGV, SIG_DFL);
808 signal(SIGTERM, SIG_DFL);
811 if (err_sig == SIGSEGV ||
812 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
814 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
823 void LyX::printError(ErrorItem const & ei)
825 docstring tmp = _("LyX: ") + ei.error + char_type(':')
827 std::cerr << to_utf8(tmp) << std::endl;
831 void LyX::initGuiFont()
833 if (lyxrc.roman_font_name.empty())
834 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
836 if (lyxrc.sans_font_name.empty())
837 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
839 if (lyxrc.typewriter_font_name.empty())
840 lyxrc.typewriter_font_name
841 = pimpl_->application_->typewriterFontName();
848 signal(SIGHUP, error_handler);
850 signal(SIGFPE, error_handler);
851 signal(SIGSEGV, error_handler);
852 signal(SIGINT, error_handler);
853 signal(SIGTERM, error_handler);
854 // SIGPIPE can be safely ignored.
856 lyxrc.tempdir_path = package().temp_dir().absFilename();
857 lyxrc.document_path = package().document_dir().absFilename();
859 if (lyxrc.template_path.empty()) {
860 lyxrc.template_path = addPath(package().system_support().absFilename(),
865 // Read configuration files
868 // This one may have been distributed along with LyX.
869 if (!readRcFile("lyxrc.dist"))
872 // Set the language defined by the distributor.
873 //setGuiLanguage(lyxrc.gui_language);
875 // Set the PATH correctly.
876 #if !defined (USE_POSIX_PACKAGING)
877 // Add the directory containing the LyX executable to the path
878 // so that LyX can find things like tex2lyx.
879 if (package().build_support().empty())
880 prependEnvPath("PATH", package().binary_dir().absFilename());
882 if (!lyxrc.path_prefix.empty())
883 prependEnvPath("PATH", lyxrc.path_prefix);
885 // Check that user LyX directory is ok.
886 if (queryUserLyXDir(package().explicit_user_support()))
887 reconfigureUserLyXDir();
889 // no need for a splash when there is no GUI
894 // This one is generated in user_support directory by lib/configure.py.
895 if (!readRcFile("lyxrc.defaults"))
898 // Query the OS to know what formats are viewed natively
899 formats.setAutoOpen();
901 // Read lyxrc.dist again to be able to override viewer auto-detection.
902 readRcFile("lyxrc.dist");
904 system_lyxrc = lyxrc;
905 system_formats = formats;
906 pimpl_->system_converters_ = pimpl_->converters_;
907 pimpl_->system_movers_ = pimpl_->movers_;
908 system_lcolor = lcolor;
910 // This one is edited through the preferences dialog.
911 if (!readRcFile("preferences"))
914 if (!readEncodingsFile("encodings", "unicodesymbols"))
916 if (!readLanguagesFile("languages"))
920 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
927 // Set the language defined by the user.
928 //setGuiLanguage(lyxrc.gui_language);
931 pimpl_->toplevel_keymap_.reset(new KeyMap);
932 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
933 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
935 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
938 if (!readUIFile(lyxrc.ui_file))
942 if (lyxerr.debugging(Debug::LYXRC))
945 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
946 if (!lyxrc.path_prefix.empty())
947 prependEnvPath("PATH", lyxrc.path_prefix);
949 FileName const document_path(lyxrc.document_path);
950 if (fs::exists(document_path.toFilesystemEncoding()) &&
951 fs::is_directory(document_path.toFilesystemEncoding()))
952 package().document_dir() = document_path;
954 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
955 if (package().temp_dir().empty()) {
956 Alert::error(_("Could not create temporary directory"),
957 bformat(_("Could not create a temporary directory in\n"
958 "%1$s. Make sure that this\n"
959 "path exists and is writable and try again."),
960 from_utf8(lyxrc.tempdir_path)));
961 // createLyXTmpDir() tries sufficiently hard to create a
962 // usable temp dir, so the probability to come here is
963 // close to zero. We therefore don't try to overcome this
964 // problem with e.g. asking the user for a new path and
965 // trying again but simply exit.
969 LYXERR(Debug::INIT) << "LyX tmp dir: `"
970 << package().temp_dir().absFilename()
973 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
974 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
976 // This must happen after package initialization and after lyxrc is
977 // read, therefore it can't be done by a static object.
978 ConverterCache::init();
984 void LyX::defaultKeyBindings(KeyMap * kbmap)
986 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
987 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
988 kbmap->bind("Up", FuncRequest(LFUN_UP));
989 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
991 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
992 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
993 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
994 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
996 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
997 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
998 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
999 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1001 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1002 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1004 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1005 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1007 // kbmap->bindings to enable the use of the numeric keypad
1008 // e.g. Num Lock set
1009 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1010 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1011 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1012 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1013 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1014 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1015 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1016 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1017 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1018 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1019 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1020 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1021 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1022 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1023 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1024 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1025 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1026 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1027 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1028 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1029 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1030 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1031 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1032 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1036 void LyX::emergencyCleanup() const
1038 // what to do about tmpfiles is non-obvious. we would
1039 // like to delete any we find, but our lyxdir might
1040 // contain documents etc. which might be helpful on
1043 pimpl_->buffer_list_.emergencyWriteAll();
1045 if (pimpl_->lyx_server_)
1046 pimpl_->lyx_server_->emergencyCleanup();
1047 pimpl_->lyx_server_.reset();
1048 pimpl_->lyx_socket_.reset();
1053 void LyX::deadKeyBindings(KeyMap * kbmap)
1055 // bindKeyings for transparent handling of deadkeys
1056 // The keysyms are gotten from XFree86 X11R6
1057 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1058 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1059 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1060 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1061 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1062 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1063 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1064 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1065 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1066 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1067 // nothing with this name
1068 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1069 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1070 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1071 // nothing with this name either...
1072 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1073 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1074 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1075 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1081 // return true if file does not exist or is older than configure.py.
1082 bool needsUpdate(string const & file)
1084 // We cannot initialize configure_script directly because the package
1085 // is not initialized yet when static objects are constructed.
1086 static string configure_script;
1087 static bool firstrun = true;
1089 configure_script = FileName(addName(
1090 package().system_support().absFilename(),
1091 "configure.py")).toFilesystemEncoding();
1095 string const absfile = FileName(addName(
1096 package().user_support().absFilename(), file)).toFilesystemEncoding();
1097 return (! fs::exists(absfile))
1098 || (fs::last_write_time(configure_script)
1099 > fs::last_write_time(absfile));
1105 bool LyX::queryUserLyXDir(bool explicit_userdir)
1107 // Does user directory exist?
1108 string const user_support =
1109 package().user_support().toFilesystemEncoding();
1110 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1111 first_start = false;
1113 return needsUpdate("lyxrc.defaults")
1114 || needsUpdate("lyxmodules.lst")
1115 || needsUpdate("textclass.lst")
1116 || needsUpdate("packages.lst");
1119 first_start = !explicit_userdir;
1121 // If the user specified explicitly a directory, ask whether
1122 // to create it. If the user says "no", then exit.
1123 if (explicit_userdir &&
1125 _("Missing user LyX directory"),
1126 bformat(_("You have specified a non-existent user "
1127 "LyX directory, %1$s.\n"
1128 "It is needed to keep your own configuration."),
1129 from_utf8(package().user_support().absFilename())),
1131 _("&Create directory"),
1133 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1134 earlyExit(EXIT_FAILURE);
1137 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1138 from_utf8(package().user_support().absFilename())))
1141 if (!createDirectory(package().user_support(), 0755)) {
1142 // Failed, so let's exit.
1143 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1145 earlyExit(EXIT_FAILURE);
1152 bool LyX::readRcFile(string const & name)
1154 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1156 FileName const lyxrc_path = libFileSearch(string(), name);
1157 if (!lyxrc_path.empty()) {
1159 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1161 if (lyxrc.read(lyxrc_path) < 0) {
1162 showFileError(name);
1166 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1172 // Read the ui file `name'
1173 bool LyX::readUIFile(string const & name, bool include)
1183 struct keyword_item uitags[ui_last - 1] = {
1184 { "include", ui_include },
1185 { "menuset", ui_menuset },
1186 { "toolbars", ui_toolbars },
1187 { "toolbarset", ui_toolbarset }
1190 // Ensure that a file is read only once (prevents include loops)
1191 static std::list<string> uifiles;
1192 std::list<string>::const_iterator it = uifiles.begin();
1193 std::list<string>::const_iterator end = uifiles.end();
1194 it = std::find(it, end, name);
1196 LYXERR(Debug::INIT) << "UI file '" << name
1197 << "' has been read already. "
1198 << "Is this an include loop?"
1203 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1208 ui_path = libFileSearch("ui", name, "inc");
1209 if (ui_path.empty())
1210 ui_path = libFileSearch("ui",
1211 changeExtension(name, "inc"));
1214 ui_path = libFileSearch("ui", name, "ui");
1216 if (ui_path.empty()) {
1217 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1218 showFileError(name);
1222 uifiles.push_back(name);
1224 LYXERR(Debug::INIT) << "Found " << name
1225 << " in " << ui_path << endl;
1226 Lexer lex(uitags, ui_last - 1);
1227 lex.setFile(ui_path);
1229 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1233 if (lyxerr.debugging(Debug::PARSER))
1234 lex.printTable(lyxerr);
1236 while (lex.isOK()) {
1237 switch (lex.lex()) {
1240 string const file = lex.getString();
1241 if (!readUIFile(file, true))
1246 menubackend.read(lex);
1250 toolbarbackend.readToolbars(lex);
1254 toolbarbackend.readToolbarSettings(lex);
1258 if (!rtrim(lex.getString()).empty())
1259 lex.printError("LyX::ReadUIFile: "
1260 "Unknown menu tag: `$$Token'");
1268 // Read the languages file `name'
1269 bool LyX::readLanguagesFile(string const & name)
1271 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1273 FileName const lang_path = libFileSearch(string(), name);
1274 if (lang_path.empty()) {
1275 showFileError(name);
1278 languages.read(lang_path);
1283 // Read the encodings file `name'
1284 bool LyX::readEncodingsFile(string const & enc_name,
1285 string const & symbols_name)
1287 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1288 << symbols_name << "..." << endl;
1290 FileName const symbols_path = libFileSearch(string(), symbols_name);
1291 if (symbols_path.empty()) {
1292 showFileError(symbols_name);
1296 FileName const enc_path = libFileSearch(string(), enc_name);
1297 if (enc_path.empty()) {
1298 showFileError(enc_name);
1301 encodings.read(enc_path, symbols_path);
1310 /// return the the number of arguments consumed
1311 typedef boost::function<int(string const &, string const &)> cmd_helper;
1313 int parse_dbg(string const & arg, string const &)
1316 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1317 Debug::showTags(lyxerr);
1320 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1322 lyxerr.level(Debug::value(arg));
1323 Debug::showLevel(lyxerr, lyxerr.level());
1328 int parse_help(string const &, string const &)
1331 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1332 "Command line switches (case sensitive):\n"
1333 "\t-help summarize LyX usage\n"
1334 "\t-userdir dir set user directory to dir\n"
1335 "\t-sysdir dir set system directory to dir\n"
1336 "\t-geometry WxH+X+Y set geometry of the main window\n"
1337 "\t-dbg feature[,feature]...\n"
1338 " select the features to debug.\n"
1339 " Type `lyx -dbg' to see the list of features\n"
1340 "\t-x [--execute] command\n"
1341 " where command is a lyx command.\n"
1342 "\t-e [--export] fmt\n"
1343 " where fmt is the export format of choice.\n"
1344 "\t-i [--import] fmt file.xxx\n"
1345 " where fmt is the import format of choice\n"
1346 " and file.xxx is the file to be imported.\n"
1347 "\t-version summarize version and build info\n"
1348 "Check the LyX man page for more details.")) << endl;
1353 int parse_version(string const &, string const &)
1355 lyxerr << "LyX " << lyx_version
1356 << " (" << lyx_release_date << ")" << endl;
1357 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1359 lyxerr << lyx_version_info << endl;
1364 int parse_sysdir(string const & arg, string const &)
1367 Alert::error(_("No system directory"),
1368 _("Missing directory for -sysdir switch"));
1371 cl_system_support = arg;
1375 int parse_userdir(string const & arg, string const &)
1378 Alert::error(_("No user directory"),
1379 _("Missing directory for -userdir switch"));
1382 cl_user_support = arg;
1386 int parse_execute(string const & arg, string const &)
1389 Alert::error(_("Incomplete command"),
1390 _("Missing command string after --execute switch"));
1397 int parse_export(string const & type, string const &)
1400 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1401 "--export switch")) << endl;
1404 batch = "buffer-export " + type;
1409 int parse_import(string const & type, string const & file)
1412 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1413 "--import switch")) << endl;
1417 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1421 batch = "buffer-import " + type + ' ' + file;
1425 int parse_geometry(string const & arg1, string const &)
1428 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1429 // remove also the arg
1432 // don't remove "-geometry"
1441 void LyX::easyParse(int & argc, char * argv[])
1443 std::map<string, cmd_helper> cmdmap;
1445 cmdmap["-dbg"] = parse_dbg;
1446 cmdmap["-help"] = parse_help;
1447 cmdmap["--help"] = parse_help;
1448 cmdmap["-version"] = parse_version;
1449 cmdmap["--version"] = parse_version;
1450 cmdmap["-sysdir"] = parse_sysdir;
1451 cmdmap["-userdir"] = parse_userdir;
1452 cmdmap["-x"] = parse_execute;
1453 cmdmap["--execute"] = parse_execute;
1454 cmdmap["-e"] = parse_export;
1455 cmdmap["--export"] = parse_export;
1456 cmdmap["-i"] = parse_import;
1457 cmdmap["--import"] = parse_import;
1458 cmdmap["-geometry"] = parse_geometry;
1460 for (int i = 1; i < argc; ++i) {
1461 std::map<string, cmd_helper>::const_iterator it
1462 = cmdmap.find(argv[i]);
1464 // don't complain if not found - may be parsed later
1465 if (it == cmdmap.end())
1468 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1469 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1471 int const remove = 1 + it->second(arg, arg2);
1473 // Now, remove used arguments by shifting
1474 // the following ones remove places down.
1477 for (int j = i; j < argc; ++j)
1478 argv[j] = argv[j + remove];
1483 batch_command = batch;
1487 FuncStatus getStatus(FuncRequest const & action)
1489 return LyX::ref().lyxFunc().getStatus(action);
1493 void dispatch(FuncRequest const & action)
1495 LyX::ref().lyxFunc().dispatch(action);
1499 BufferList & theBufferList()
1501 return LyX::ref().bufferList();
1505 LyXFunc & theLyXFunc()
1507 return LyX::ref().lyxFunc();
1511 Server & theServer()
1513 // FIXME: this should not be use_gui dependent
1514 BOOST_ASSERT(use_gui);
1515 return LyX::ref().server();
1519 ServerSocket & theServerSocket()
1521 // FIXME: this should not be use_gui dependent
1522 BOOST_ASSERT(use_gui);
1523 return LyX::ref().socket();
1527 KeyMap & theTopLevelKeymap()
1529 BOOST_ASSERT(use_gui);
1530 return LyX::ref().topLevelKeymap();
1534 Converters & theConverters()
1536 return LyX::ref().converters();
1540 Converters & theSystemConverters()
1542 return LyX::ref().systemConverters();
1546 Movers & theMovers()
1548 return LyX::ref().pimpl_->movers_;
1552 Mover const & getMover(std::string const & fmt)
1554 return LyX::ref().pimpl_->movers_(fmt);
1558 void setMover(std::string const & fmt, std::string const & command)
1560 LyX::ref().pimpl_->movers_.set(fmt, command);
1564 Movers & theSystemMovers()
1566 return LyX::ref().pimpl_->system_movers_;
1570 Messages & getMessages(std::string const & language)
1572 return LyX::ref().getMessages(language);
1576 Messages & getGuiMessages()
1578 return LyX::ref().getGuiMessages();