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 // Force adding of font path _before_ Application is initialized
454 support::os::addFontResources();
456 // Let the frontend parse and remove all arguments that it knows
457 pimpl_->application_.reset(createApplication(argc, argv));
461 // Parse and remove all known arguments in the LyX singleton
462 // Give an error for all remaining ones.
463 int exit_status = init(argc, argv);
465 // Kill the application object before exiting.
466 pimpl_->application_.reset();
473 /* Create a CoreApplication class that will provide the main event loop
474 * and the socket callback registering. With Qt4, only QtCore
475 * library would be needed.
476 * When this is done, a server_mode could be created and the following two
477 * line would be moved out from here.
479 // Note: socket callback must be registered after init(argc, argv)
480 // such that package().temp_dir() is properly initialized.
481 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
482 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
483 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
485 // Start the real execution loop.
486 exit_status = pimpl_->application_->exec();
490 // Restore original font resources after Application is destroyed.
491 support::os::restoreFontResources();
497 void LyX::prepareExit()
499 // Set a flag that we do quitting from the program,
500 // so no refreshes are necessary.
503 // close buffers first
504 pimpl_->buffer_list_.closeAll();
506 // do any other cleanup procedures now
507 if (package().temp_dir() != package().system_temp_dir()) {
508 LYXERR(Debug::INFO) << "Deleting tmp dir "
509 << package().temp_dir().absFilename() << endl;
511 if (!destroyDir(package().temp_dir())) {
512 docstring const msg =
513 bformat(_("Unable to remove the temporary directory %1$s"),
514 from_utf8(package().temp_dir().absFilename()));
515 Alert::warning(_("Unable to remove temporary directory"), msg);
520 if (pimpl_->session_)
521 pimpl_->session_->writeFile();
522 pimpl_->session_.reset();
523 pimpl_->lyx_server_.reset();
524 pimpl_->lyx_socket_.reset();
527 // Kill the application object before exiting. This avoids crashes
528 // when exiting on Linux.
529 if (pimpl_->application_)
530 pimpl_->application_.reset();
534 void LyX::earlyExit(int status)
536 BOOST_ASSERT(pimpl_->application_.get());
537 // LyX::pimpl_::application_ is not initialised at this
538 // point so it's safe to just exit after some cleanup.
544 int LyX::init(int & argc, char * argv[])
546 // check for any spurious extra arguments
547 // other than documents
548 for (int argi = 1; argi < argc ; ++argi) {
549 if (argv[argi][0] == '-') {
551 bformat(_("Wrong command line option `%1$s'. Exiting."),
552 from_utf8(argv[argi]))) << endl;
557 // Initialization of LyX (reads lyxrc and more)
558 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
559 bool success = init();
560 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
564 for (int argi = argc - 1; argi >= 1; --argi) {
565 // get absolute path of file and add ".lyx" to
566 // the filename if necessary
567 pimpl_->files_to_load_.push_back(fileSearch(string(),
568 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
569 "lyx", support::allow_unreadable));
573 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
579 void LyX::addFileToLoad(FileName const & fname)
581 vector<FileName>::const_iterator cit = std::find(
582 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
585 if (cit == pimpl_->files_to_load_.end())
586 pimpl_->files_to_load_.push_back(fname);
590 void LyX::loadFiles()
592 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
593 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
595 for (; it != end; ++it) {
599 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
600 if (loadLyXFile(buf, *it)) {
601 ErrorList const & el = buf->errorList("Parse");
603 for_each(el.begin(), el.end(),
604 boost::bind(&LyX::printError, this, _1));
607 pimpl_->buffer_list_.release(buf);
612 void LyX::execBatchCommands()
614 // The advantage of doing this here is that the event loop
615 // is already started. So any need for interaction will be
619 // Execute batch commands if available
620 if (batch_command.empty())
623 LYXERR(Debug::INIT) << "About to handle -x '"
624 << batch_command << '\'' << endl;
626 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
630 void LyX::restoreGuiSession()
632 LyXView * view = newLyXView();
634 // if some files were specified at command-line we assume that the
635 // user wants to edit *these* files and not to restore the session.
636 if (!pimpl_->files_to_load_.empty()) {
637 for_each(pimpl_->files_to_load_.begin(),
638 pimpl_->files_to_load_.end(),
639 bind(&LyXView::loadLyXFile, view, _1, true));
640 // clear this list to save a few bytes of RAM
641 pimpl_->files_to_load_.clear();
642 pimpl_->session_->lastOpened().clear();
644 } else if (lyxrc.load_session) {
645 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
646 // do not add to the lastfile list since these files are restored from
647 // last session, and should be already there (regular files), or should
648 // not be added at all (help files).
649 for_each(lastopened.begin(), lastopened.end(),
650 bind(&LyXView::loadLyXFile, view, _1, false));
652 // clear this list to save a few bytes of RAM
653 pimpl_->session_->lastOpened().clear();
656 BufferList::iterator I = pimpl_->buffer_list_.begin();
657 BufferList::iterator end = pimpl_->buffer_list_.end();
658 for (; I != end; ++I) {
660 if (buf != buf->getMasterBuffer())
665 // FIXME: Switch to the last loaded Buffer. This must not be the first one
666 // because the Buffer won't be connected in this case. The correct solution
667 // would be to avoid the manual connection of the current Buffer in LyXView.
668 if (!pimpl_->buffer_list_.empty())
669 view->setBuffer(pimpl_->buffer_list_.last());
673 LyXView * LyX::newLyXView()
678 // determine windows size and position, from lyxrc and/or session
680 unsigned int width = 690;
681 unsigned int height = 510;
682 // default icon size, will be overwritten by stored session value
683 unsigned int iconSizeXY = 0;
684 int maximized = LyXView::NotMaximized;
686 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
687 width = lyxrc.geometry_width;
688 height = lyxrc.geometry_height;
690 // if lyxrc returns (0,0), then use session info
692 string val = session().sessionInfo().load("WindowWidth");
694 width = convert<unsigned int>(val);
695 val = session().sessionInfo().load("WindowHeight");
697 height = convert<unsigned int>(val);
698 val = session().sessionInfo().load("WindowMaximized");
700 maximized = convert<int>(val);
701 val = session().sessionInfo().load("IconSizeXY");
703 iconSizeXY = convert<unsigned int>(val);
706 // if user wants to restore window position
709 if (lyxrc.geometry_xysaved) {
710 string val = session().sessionInfo().load("WindowPosX");
712 posx = convert<int>(val);
713 val = session().sessionInfo().load("WindowPosY");
715 posy = convert<int>(val);
718 if (!geometryArg.empty())
724 // create the main window
725 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
733 The SIGHUP signal does not exist on Windows and does not need to be handled.
735 Windows handles SIGFPE and SIGSEGV signals as expected.
737 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
738 cause a new thread to be spawned. This may well result in unexpected
739 behaviour by the single-threaded LyX.
741 SIGTERM signals will come only from another process actually sending
742 that signal using 'raise' in Windows' POSIX compatability layer. It will
743 not come from the general "terminate process" methods that everyone
744 actually uses (and which can't be trapped). Killing an app 'politely' on
745 Windows involves first sending a WM_CLOSE message, something that is
746 caught already by the Qt frontend.
748 For more information see:
750 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
751 ...signals are mostly useless on Windows for a variety of reasons that are
754 'UNIX Application Migration Guide, Chapter 9'
755 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
757 'How To Terminate an Application "Cleanly" in Win32'
758 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
762 static void error_handler(int err_sig)
764 // Throw away any signals other than the first one received.
765 static sig_atomic_t handling_error = false;
768 handling_error = true;
770 // We have received a signal indicating a fatal error, so
771 // try and save the data ASAP.
772 LyX::cref().emergencyCleanup();
774 // These lyxerr calls may or may not work:
776 // Signals are asynchronous, so the main program may be in a very
777 // fragile state when a signal is processed and thus while a signal
778 // handler function executes.
779 // In general, therefore, we should avoid performing any
780 // I/O operations or calling most library and system functions from
783 // This shouldn't matter here, however, as we've already invoked
788 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
792 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
795 lyxerr << "\nlyx: SIGSEGV signal caught\n"
796 "Sorry, you have found a bug in LyX. "
797 "Please read the bug-reporting instructions "
798 "in Help->Introduction and send us a bug report, "
799 "if necessary. Thanks !\nBye." << endl;
807 // Deinstall the signal handlers
809 signal(SIGHUP, SIG_DFL);
811 signal(SIGINT, SIG_DFL);
812 signal(SIGFPE, SIG_DFL);
813 signal(SIGSEGV, SIG_DFL);
814 signal(SIGTERM, SIG_DFL);
817 if (err_sig == SIGSEGV ||
818 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
820 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
829 void LyX::printError(ErrorItem const & ei)
831 docstring tmp = _("LyX: ") + ei.error + char_type(':')
833 std::cerr << to_utf8(tmp) << std::endl;
837 void LyX::initGuiFont()
839 if (lyxrc.roman_font_name.empty())
840 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
842 if (lyxrc.sans_font_name.empty())
843 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
845 if (lyxrc.typewriter_font_name.empty())
846 lyxrc.typewriter_font_name
847 = pimpl_->application_->typewriterFontName();
854 signal(SIGHUP, error_handler);
856 signal(SIGFPE, error_handler);
857 signal(SIGSEGV, error_handler);
858 signal(SIGINT, error_handler);
859 signal(SIGTERM, error_handler);
860 // SIGPIPE can be safely ignored.
862 lyxrc.tempdir_path = package().temp_dir().absFilename();
863 lyxrc.document_path = package().document_dir().absFilename();
865 if (lyxrc.template_path.empty()) {
866 lyxrc.template_path = addPath(package().system_support().absFilename(),
871 // Read configuration files
874 // This one may have been distributed along with LyX.
875 if (!readRcFile("lyxrc.dist"))
878 // Set the language defined by the distributor.
879 //setGuiLanguage(lyxrc.gui_language);
881 // Set the PATH correctly.
882 #if !defined (USE_POSIX_PACKAGING)
883 // Add the directory containing the LyX executable to the path
884 // so that LyX can find things like tex2lyx.
885 if (package().build_support().empty())
886 prependEnvPath("PATH", package().binary_dir().absFilename());
888 if (!lyxrc.path_prefix.empty())
889 prependEnvPath("PATH", lyxrc.path_prefix);
891 // Check that user LyX directory is ok.
892 if (queryUserLyXDir(package().explicit_user_support()))
893 reconfigureUserLyXDir();
895 // no need for a splash when there is no GUI
900 // This one is generated in user_support directory by lib/configure.py.
901 if (!readRcFile("lyxrc.defaults"))
904 // Query the OS to know what formats are viewed natively
905 formats.setAutoOpen();
907 // Read lyxrc.dist again to be able to override viewer auto-detection.
908 readRcFile("lyxrc.dist");
910 system_lyxrc = lyxrc;
911 system_formats = formats;
912 pimpl_->system_converters_ = pimpl_->converters_;
913 pimpl_->system_movers_ = pimpl_->movers_;
914 system_lcolor = lcolor;
916 // This one is edited through the preferences dialog.
917 if (!readRcFile("preferences"))
920 if (!readEncodingsFile("encodings", "unicodesymbols"))
922 if (!readLanguagesFile("languages"))
926 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
933 // Set the language defined by the user.
934 //setGuiLanguage(lyxrc.gui_language);
937 pimpl_->toplevel_keymap_.reset(new KeyMap);
938 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
939 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
941 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
944 if (!readUIFile(lyxrc.ui_file))
948 if (lyxerr.debugging(Debug::LYXRC))
951 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
952 if (!lyxrc.path_prefix.empty())
953 prependEnvPath("PATH", lyxrc.path_prefix);
955 FileName const document_path(lyxrc.document_path);
956 if (fs::exists(document_path.toFilesystemEncoding()) &&
957 fs::is_directory(document_path.toFilesystemEncoding()))
958 package().document_dir() = document_path;
960 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
961 if (package().temp_dir().empty()) {
962 Alert::error(_("Could not create temporary directory"),
963 bformat(_("Could not create a temporary directory in\n"
964 "%1$s. Make sure that this\n"
965 "path exists and is writable and try again."),
966 from_utf8(lyxrc.tempdir_path)));
967 // createLyXTmpDir() tries sufficiently hard to create a
968 // usable temp dir, so the probability to come here is
969 // close to zero. We therefore don't try to overcome this
970 // problem with e.g. asking the user for a new path and
971 // trying again but simply exit.
975 LYXERR(Debug::INIT) << "LyX tmp dir: `"
976 << package().temp_dir().absFilename()
979 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
980 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
982 // This must happen after package initialization and after lyxrc is
983 // read, therefore it can't be done by a static object.
984 ConverterCache::init();
990 void LyX::defaultKeyBindings(KeyMap * kbmap)
992 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
993 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
994 kbmap->bind("Up", FuncRequest(LFUN_UP));
995 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
997 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
998 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
999 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
1000 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
1002 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1003 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1004 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1005 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1007 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1008 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1010 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1011 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1013 // kbmap->bindings to enable the use of the numeric keypad
1014 // e.g. Num Lock set
1015 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1016 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1017 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1018 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1019 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1020 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1021 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1022 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1023 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1024 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1025 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1026 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1027 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1028 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1029 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1030 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1031 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1032 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1033 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1034 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1035 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1036 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1037 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1038 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1042 void LyX::emergencyCleanup() const
1044 // what to do about tmpfiles is non-obvious. we would
1045 // like to delete any we find, but our lyxdir might
1046 // contain documents etc. which might be helpful on
1049 pimpl_->buffer_list_.emergencyWriteAll();
1051 if (pimpl_->lyx_server_)
1052 pimpl_->lyx_server_->emergencyCleanup();
1053 pimpl_->lyx_server_.reset();
1054 pimpl_->lyx_socket_.reset();
1059 void LyX::deadKeyBindings(KeyMap * kbmap)
1061 // bindKeyings for transparent handling of deadkeys
1062 // The keysyms are gotten from XFree86 X11R6
1063 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1064 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1065 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1066 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1067 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1068 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1069 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1070 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1071 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1072 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1073 // nothing with this name
1074 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1075 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1076 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1077 // nothing with this name either...
1078 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1079 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1080 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1081 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1087 // return true if file does not exist or is older than configure.py.
1088 bool needsUpdate(string const & file)
1090 // We cannot initialize configure_script directly because the package
1091 // is not initialized yet when static objects are constructed.
1092 static string configure_script;
1093 static bool firstrun = true;
1095 configure_script = FileName(addName(
1096 package().system_support().absFilename(),
1097 "configure.py")).toFilesystemEncoding();
1101 string const absfile = FileName(addName(
1102 package().user_support().absFilename(), file)).toFilesystemEncoding();
1103 return (! fs::exists(absfile))
1104 || (fs::last_write_time(configure_script)
1105 > fs::last_write_time(absfile));
1111 bool LyX::queryUserLyXDir(bool explicit_userdir)
1113 // Does user directory exist?
1114 string const user_support =
1115 package().user_support().toFilesystemEncoding();
1116 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1117 first_start = false;
1119 return needsUpdate("lyxrc.defaults")
1120 || needsUpdate("lyxmodules.lst")
1121 || needsUpdate("textclass.lst")
1122 || needsUpdate("packages.lst");
1125 first_start = !explicit_userdir;
1127 // If the user specified explicitly a directory, ask whether
1128 // to create it. If the user says "no", then exit.
1129 if (explicit_userdir &&
1131 _("Missing user LyX directory"),
1132 bformat(_("You have specified a non-existent user "
1133 "LyX directory, %1$s.\n"
1134 "It is needed to keep your own configuration."),
1135 from_utf8(package().user_support().absFilename())),
1137 _("&Create directory"),
1139 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1140 earlyExit(EXIT_FAILURE);
1143 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1144 from_utf8(package().user_support().absFilename())))
1147 if (!createDirectory(package().user_support(), 0755)) {
1148 // Failed, so let's exit.
1149 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1151 earlyExit(EXIT_FAILURE);
1158 bool LyX::readRcFile(string const & name)
1160 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1162 FileName const lyxrc_path = libFileSearch(string(), name);
1163 if (!lyxrc_path.empty()) {
1165 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1167 if (lyxrc.read(lyxrc_path) < 0) {
1168 showFileError(name);
1172 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1178 // Read the ui file `name'
1179 bool LyX::readUIFile(string const & name, bool include)
1189 struct keyword_item uitags[ui_last - 1] = {
1190 { "include", ui_include },
1191 { "menuset", ui_menuset },
1192 { "toolbars", ui_toolbars },
1193 { "toolbarset", ui_toolbarset }
1196 // Ensure that a file is read only once (prevents include loops)
1197 static std::list<string> uifiles;
1198 std::list<string>::const_iterator it = uifiles.begin();
1199 std::list<string>::const_iterator end = uifiles.end();
1200 it = std::find(it, end, name);
1202 LYXERR(Debug::INIT) << "UI file '" << name
1203 << "' has been read already. "
1204 << "Is this an include loop?"
1209 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1214 ui_path = libFileSearch("ui", name, "inc");
1215 if (ui_path.empty())
1216 ui_path = libFileSearch("ui",
1217 changeExtension(name, "inc"));
1220 ui_path = libFileSearch("ui", name, "ui");
1222 if (ui_path.empty()) {
1223 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1224 showFileError(name);
1228 uifiles.push_back(name);
1230 LYXERR(Debug::INIT) << "Found " << name
1231 << " in " << ui_path << endl;
1232 Lexer lex(uitags, ui_last - 1);
1233 lex.setFile(ui_path);
1235 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1239 if (lyxerr.debugging(Debug::PARSER))
1240 lex.printTable(lyxerr);
1242 while (lex.isOK()) {
1243 switch (lex.lex()) {
1246 string const file = lex.getString();
1247 if (!readUIFile(file, true))
1252 menubackend.read(lex);
1256 toolbarbackend.readToolbars(lex);
1260 toolbarbackend.readToolbarSettings(lex);
1264 if (!rtrim(lex.getString()).empty())
1265 lex.printError("LyX::ReadUIFile: "
1266 "Unknown menu tag: `$$Token'");
1274 // Read the languages file `name'
1275 bool LyX::readLanguagesFile(string const & name)
1277 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1279 FileName const lang_path = libFileSearch(string(), name);
1280 if (lang_path.empty()) {
1281 showFileError(name);
1284 languages.read(lang_path);
1289 // Read the encodings file `name'
1290 bool LyX::readEncodingsFile(string const & enc_name,
1291 string const & symbols_name)
1293 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1294 << symbols_name << "..." << endl;
1296 FileName const symbols_path = libFileSearch(string(), symbols_name);
1297 if (symbols_path.empty()) {
1298 showFileError(symbols_name);
1302 FileName const enc_path = libFileSearch(string(), enc_name);
1303 if (enc_path.empty()) {
1304 showFileError(enc_name);
1307 encodings.read(enc_path, symbols_path);
1316 /// return the the number of arguments consumed
1317 typedef boost::function<int(string const &, string const &)> cmd_helper;
1319 int parse_dbg(string const & arg, string const &)
1322 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1323 Debug::showTags(lyxerr);
1326 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1328 lyxerr.level(Debug::value(arg));
1329 Debug::showLevel(lyxerr, lyxerr.level());
1334 int parse_help(string const &, string const &)
1337 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1338 "Command line switches (case sensitive):\n"
1339 "\t-help summarize LyX usage\n"
1340 "\t-userdir dir set user directory to dir\n"
1341 "\t-sysdir dir set system directory to dir\n"
1342 "\t-geometry WxH+X+Y set geometry of the main window\n"
1343 "\t-dbg feature[,feature]...\n"
1344 " select the features to debug.\n"
1345 " Type `lyx -dbg' to see the list of features\n"
1346 "\t-x [--execute] command\n"
1347 " where command is a lyx command.\n"
1348 "\t-e [--export] fmt\n"
1349 " where fmt is the export format of choice.\n"
1350 "\t-i [--import] fmt file.xxx\n"
1351 " where fmt is the import format of choice\n"
1352 " and file.xxx is the file to be imported.\n"
1353 "\t-version summarize version and build info\n"
1354 "Check the LyX man page for more details.")) << endl;
1359 int parse_version(string const &, string const &)
1361 lyxerr << "LyX " << lyx_version
1362 << " (" << lyx_release_date << ")" << endl;
1363 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1365 lyxerr << lyx_version_info << endl;
1370 int parse_sysdir(string const & arg, string const &)
1373 Alert::error(_("No system directory"),
1374 _("Missing directory for -sysdir switch"));
1377 cl_system_support = arg;
1381 int parse_userdir(string const & arg, string const &)
1384 Alert::error(_("No user directory"),
1385 _("Missing directory for -userdir switch"));
1388 cl_user_support = arg;
1392 int parse_execute(string const & arg, string const &)
1395 Alert::error(_("Incomplete command"),
1396 _("Missing command string after --execute switch"));
1403 int parse_export(string const & type, string const &)
1406 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1407 "--export switch")) << endl;
1410 batch = "buffer-export " + type;
1415 int parse_import(string const & type, string const & file)
1418 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1419 "--import switch")) << endl;
1423 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1427 batch = "buffer-import " + type + ' ' + file;
1431 int parse_geometry(string const & arg1, string const &)
1434 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1435 // remove also the arg
1438 // don't remove "-geometry"
1447 void LyX::easyParse(int & argc, char * argv[])
1449 std::map<string, cmd_helper> cmdmap;
1451 cmdmap["-dbg"] = parse_dbg;
1452 cmdmap["-help"] = parse_help;
1453 cmdmap["--help"] = parse_help;
1454 cmdmap["-version"] = parse_version;
1455 cmdmap["--version"] = parse_version;
1456 cmdmap["-sysdir"] = parse_sysdir;
1457 cmdmap["-userdir"] = parse_userdir;
1458 cmdmap["-x"] = parse_execute;
1459 cmdmap["--execute"] = parse_execute;
1460 cmdmap["-e"] = parse_export;
1461 cmdmap["--export"] = parse_export;
1462 cmdmap["-i"] = parse_import;
1463 cmdmap["--import"] = parse_import;
1464 cmdmap["-geometry"] = parse_geometry;
1466 for (int i = 1; i < argc; ++i) {
1467 std::map<string, cmd_helper>::const_iterator it
1468 = cmdmap.find(argv[i]);
1470 // don't complain if not found - may be parsed later
1471 if (it == cmdmap.end())
1474 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1475 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1477 int const remove = 1 + it->second(arg, arg2);
1479 // Now, remove used arguments by shifting
1480 // the following ones remove places down.
1483 for (int j = i; j < argc; ++j)
1484 argv[j] = argv[j + remove];
1489 batch_command = batch;
1493 FuncStatus getStatus(FuncRequest const & action)
1495 return LyX::ref().lyxFunc().getStatus(action);
1499 void dispatch(FuncRequest const & action)
1501 LyX::ref().lyxFunc().dispatch(action);
1505 BufferList & theBufferList()
1507 return LyX::ref().bufferList();
1511 LyXFunc & theLyXFunc()
1513 return LyX::ref().lyxFunc();
1517 Server & theServer()
1519 // FIXME: this should not be use_gui dependent
1520 BOOST_ASSERT(use_gui);
1521 return LyX::ref().server();
1525 ServerSocket & theServerSocket()
1527 // FIXME: this should not be use_gui dependent
1528 BOOST_ASSERT(use_gui);
1529 return LyX::ref().socket();
1533 KeyMap & theTopLevelKeymap()
1535 BOOST_ASSERT(use_gui);
1536 return LyX::ref().topLevelKeymap();
1540 Converters & theConverters()
1542 return LyX::ref().converters();
1546 Converters & theSystemConverters()
1548 return LyX::ref().systemConverters();
1552 Movers & theMovers()
1554 return LyX::ref().pimpl_->movers_;
1558 Mover const & getMover(std::string const & fmt)
1560 return LyX::ref().pimpl_->movers_(fmt);
1564 void setMover(std::string const & fmt, std::string const & command)
1566 LyX::ref().pimpl_->movers_.set(fmt, command);
1570 Movers & theSystemMovers()
1572 return LyX::ref().pimpl_->system_movers_;
1576 Messages & getMessages(std::string const & language)
1578 return LyX::ref().getMessages(language);
1582 Messages & getGuiMessages()
1584 return LyX::ref().getGuiMessages();