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"
25 #include "CutAndPaste.h"
28 #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.
120 bool quitting; // flag, that we are quitting the program
124 // Filled with the command line arguments "foo" of "-sysdir foo" or
126 string cl_system_support;
127 string cl_user_support;
129 std::string geometryArg;
131 LyX * singleton_ = 0;
133 void showFileError(string const & error)
135 Alert::warning(_("Could not read configuration file"),
136 bformat(_("Error while reading the configuration file\n%1$s.\n"
137 "Please check your installation."), from_utf8(error)));
141 void reconfigureUserLyXDir()
143 string const configure_command = package().configure_command();
145 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
146 support::Path p(package().user_support());
148 one.startscript(Systemcall::Wait, configure_command);
149 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
155 /// The main application class private implementation.
156 struct LyX::Singletons
160 // Set the default User Interface language as soon as possible.
161 // The language used will be derived from the environment
163 messages_["GUI"] = Messages();
165 /// our function handler
168 BufferList buffer_list_;
170 boost::scoped_ptr<KeyMap> toplevel_keymap_;
172 boost::scoped_ptr<Server> lyx_server_;
174 boost::scoped_ptr<ServerSocket> lyx_socket_;
176 boost::scoped_ptr<frontend::Application> application_;
177 /// lyx session, containing lastfiles, lastfilepos, and lastopened
178 boost::scoped_ptr<Session> session_;
180 /// Files to load at start.
181 vector<FileName> files_to_load_;
183 /// The messages translators.
184 map<string, Messages> messages_;
186 /// The file converters.
187 Converters converters_;
189 // The system converters copy after reading lyxrc.defaults.
190 Converters system_converters_;
196 Movers system_movers_;
200 frontend::Application * theApp()
203 return singleton_->pimpl_->application_.get();
216 BOOST_ASSERT(singleton_);
221 LyX const & LyX::cref()
223 BOOST_ASSERT(singleton_);
232 pimpl_.reset(new Singletons);
236 BufferList & LyX::bufferList()
238 return pimpl_->buffer_list_;
242 BufferList const & LyX::bufferList() const
244 return pimpl_->buffer_list_;
248 Session & LyX::session()
250 BOOST_ASSERT(pimpl_->session_.get());
251 return *pimpl_->session_.get();
255 Session const & LyX::session() const
257 BOOST_ASSERT(pimpl_->session_.get());
258 return *pimpl_->session_.get();
262 LyXFunc & LyX::lyxFunc()
264 return pimpl_->lyxfunc_;
268 LyXFunc const & LyX::lyxFunc() const
270 return pimpl_->lyxfunc_;
274 Server & LyX::server()
276 BOOST_ASSERT(pimpl_->lyx_server_.get());
277 return *pimpl_->lyx_server_.get();
281 Server const & LyX::server() const
283 BOOST_ASSERT(pimpl_->lyx_server_.get());
284 return *pimpl_->lyx_server_.get();
288 ServerSocket & LyX::socket()
290 BOOST_ASSERT(pimpl_->lyx_socket_.get());
291 return *pimpl_->lyx_socket_.get();
295 ServerSocket const & LyX::socket() const
297 BOOST_ASSERT(pimpl_->lyx_socket_.get());
298 return *pimpl_->lyx_socket_.get();
302 frontend::Application & LyX::application()
304 BOOST_ASSERT(pimpl_->application_.get());
305 return *pimpl_->application_.get();
309 frontend::Application const & LyX::application() const
311 BOOST_ASSERT(pimpl_->application_.get());
312 return *pimpl_->application_.get();
316 KeyMap & LyX::topLevelKeymap()
318 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
319 return *pimpl_->toplevel_keymap_.get();
323 Converters & LyX::converters()
325 return pimpl_->converters_;
329 Converters & LyX::systemConverters()
331 return pimpl_->system_converters_;
335 KeyMap const & LyX::topLevelKeymap() const
337 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
338 return *pimpl_->toplevel_keymap_.get();
342 Messages & LyX::getMessages(std::string const & language)
344 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
346 if (it != pimpl_->messages_.end())
349 std::pair<map<string, Messages>::iterator, bool> result =
350 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
352 BOOST_ASSERT(result.second);
353 return result.first->second;
357 Messages & LyX::getGuiMessages()
359 return pimpl_->messages_["GUI"];
363 void LyX::setGuiLanguage(std::string const & language)
365 pimpl_->messages_["GUI"] = Messages(language);
369 Buffer const * LyX::updateInset(Inset const * inset) const
371 if (quitting || !inset)
374 Buffer const * buffer_ptr = 0;
375 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
376 vector<int>::const_iterator it = view_ids.begin();
377 vector<int>::const_iterator const end = view_ids.end();
378 for (; it != end; ++it) {
380 pimpl_->application_->gui().view(*it).updateInset(inset);
388 void LyX::hideDialogs(std::string const & name, Inset * inset) const
390 if (quitting || !use_gui)
393 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
394 vector<int>::const_iterator it = view_ids.begin();
395 vector<int>::const_iterator const end = view_ids.end();
396 for (; it != end; ++it)
397 pimpl_->application_->gui().view(*it).getDialogs().
402 int LyX::exec(int & argc, char * argv[])
404 // Here we need to parse the command line. At least
405 // we need to parse for "-dbg" and "-help"
406 easyParse(argc, argv);
409 support::init_package(to_utf8(from_local8bit(argv[0])),
410 cl_system_support, cl_user_support,
411 support::top_build_dir_is_one_level_up);
412 } catch (support::ExceptionMessage const & message) {
413 if (message.type_ == support::ErrorException) {
414 Alert::error(message.title_, message.details_);
416 } else if (message.type_ == support::WarningException) {
417 Alert::warning(message.title_, message.details_);
421 // Reinit the messages machinery in case package() knows
422 // something interesting about the locale directory.
426 // FIXME: create a ConsoleApplication
427 int exit_status = init(argc, argv);
435 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
440 BufferList::iterator begin = pimpl_->buffer_list_.begin();
442 bool final_success = false;
443 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
445 if (buf != buf->getMasterBuffer())
447 bool success = false;
448 buf->dispatch(batch_command, &success);
449 final_success |= success;
452 return !final_success;
455 // Let the frontend parse and remove all arguments that it knows
456 pimpl_->application_.reset(createApplication(argc, argv));
460 // Parse and remove all known arguments in the LyX singleton
461 // Give an error for all remaining ones.
462 int exit_status = init(argc, argv);
464 // Kill the application object before exiting.
465 pimpl_->application_.reset();
472 /* Create a CoreApplication class that will provide the main event loop
473 * and the socket callback registering. With Qt4, only QtCore
474 * library would be needed.
475 * When this is done, a server_mode could be created and the following two
476 * line would be moved out from here.
478 // Note: socket callback must be registered after init(argc, argv)
479 // such that package().temp_dir() is properly initialized.
480 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
481 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
482 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
484 // Start the real execution loop.
485 exit_status = pimpl_->application_->exec();
493 void LyX::prepareExit()
495 // Clear the clipboard and selection stack:
496 cap::clearCutStack();
497 cap::clearSelection();
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 // if reconfiguration is needed.
620 if (textclasslist.empty()) {
621 switch (Alert::prompt(
622 _("No textclass is found"),
623 _("LyX cannot continue because no textclass is found. "
624 "You can either reconfigure normally, or reconfigure using "
625 "default textclasses, or quit LyX."),
632 // regular reconfigure
633 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
636 // reconfigure --without-latex-config
637 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
638 " --without-latex-config"));
641 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
645 // Execute batch commands if available
646 if (batch_command.empty())
649 LYXERR(Debug::INIT) << "About to handle -x '"
650 << batch_command << '\'' << endl;
652 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
656 void LyX::restoreGuiSession()
658 LyXView * view = newLyXView();
660 // if there is no valid class list, do not load any file.
661 if (textclasslist.empty())
664 // if some files were specified at command-line we assume that the
665 // user wants to edit *these* files and not to restore the session.
666 if (!pimpl_->files_to_load_.empty()) {
667 for_each(pimpl_->files_to_load_.begin(),
668 pimpl_->files_to_load_.end(),
669 bind(&LyXView::loadLyXFile, view, _1, true));
670 // clear this list to save a few bytes of RAM
671 pimpl_->files_to_load_.clear();
672 pimpl_->session_->lastOpened().clear();
674 } else if (lyxrc.load_session) {
675 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
676 // do not add to the lastfile list since these files are restored from
677 // last session, and should be already there (regular files), or should
678 // not be added at all (help files).
679 for_each(lastopened.begin(), lastopened.end(),
680 bind(&LyXView::loadLyXFile, view, _1, false));
682 // clear this list to save a few bytes of RAM
683 pimpl_->session_->lastOpened().clear();
686 BufferList::iterator I = pimpl_->buffer_list_.begin();
687 BufferList::iterator end = pimpl_->buffer_list_.end();
688 for (; I != end; ++I) {
690 if (buf != buf->getMasterBuffer())
695 // FIXME: Switch to the last loaded Buffer. This must not be the first one
696 // because the Buffer won't be connected in this case. The correct solution
697 // would be to avoid the manual connection of the current Buffer in LyXView.
698 if (!pimpl_->buffer_list_.empty())
699 view->setBuffer(pimpl_->buffer_list_.last());
703 LyXView * LyX::newLyXView()
708 // determine windows size and position, from lyxrc and/or session
710 unsigned int width = 690;
711 unsigned int height = 510;
712 // default icon size, will be overwritten by stored session value
713 unsigned int iconSizeXY = 0;
714 int maximized = LyXView::NotMaximized;
716 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
717 width = lyxrc.geometry_width;
718 height = lyxrc.geometry_height;
720 // if lyxrc returns (0,0), then use session info
722 string val = session().sessionInfo().load("WindowWidth");
724 width = convert<unsigned int>(val);
725 val = session().sessionInfo().load("WindowHeight");
727 height = convert<unsigned int>(val);
728 val = session().sessionInfo().load("WindowMaximized");
730 maximized = convert<int>(val);
731 val = session().sessionInfo().load("IconSizeXY");
733 iconSizeXY = convert<unsigned int>(val);
736 // if user wants to restore window position
739 if (lyxrc.geometry_xysaved) {
740 string val = session().sessionInfo().load("WindowPosX");
742 posx = convert<int>(val);
743 val = session().sessionInfo().load("WindowPosY");
745 posy = convert<int>(val);
748 if (!geometryArg.empty())
754 // create the main window
755 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
763 The SIGHUP signal does not exist on Windows and does not need to be handled.
765 Windows handles SIGFPE and SIGSEGV signals as expected.
767 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
768 cause a new thread to be spawned. This may well result in unexpected
769 behaviour by the single-threaded LyX.
771 SIGTERM signals will come only from another process actually sending
772 that signal using 'raise' in Windows' POSIX compatability layer. It will
773 not come from the general "terminate process" methods that everyone
774 actually uses (and which can't be trapped). Killing an app 'politely' on
775 Windows involves first sending a WM_CLOSE message, something that is
776 caught already by the Qt frontend.
778 For more information see:
780 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
781 ...signals are mostly useless on Windows for a variety of reasons that are
784 'UNIX Application Migration Guide, Chapter 9'
785 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
787 'How To Terminate an Application "Cleanly" in Win32'
788 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
792 static void error_handler(int err_sig)
794 // Throw away any signals other than the first one received.
795 static sig_atomic_t handling_error = false;
798 handling_error = true;
800 // We have received a signal indicating a fatal error, so
801 // try and save the data ASAP.
802 LyX::cref().emergencyCleanup();
804 // These lyxerr calls may or may not work:
806 // Signals are asynchronous, so the main program may be in a very
807 // fragile state when a signal is processed and thus while a signal
808 // handler function executes.
809 // In general, therefore, we should avoid performing any
810 // I/O operations or calling most library and system functions from
813 // This shouldn't matter here, however, as we've already invoked
818 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
822 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
825 lyxerr << "\nlyx: SIGSEGV signal caught\n"
826 "Sorry, you have found a bug in LyX. "
827 "Please read the bug-reporting instructions "
828 "in Help->Introduction and send us a bug report, "
829 "if necessary. Thanks !\nBye." << endl;
837 // Deinstall the signal handlers
839 signal(SIGHUP, SIG_DFL);
841 signal(SIGINT, SIG_DFL);
842 signal(SIGFPE, SIG_DFL);
843 signal(SIGSEGV, SIG_DFL);
844 signal(SIGTERM, SIG_DFL);
847 if (err_sig == SIGSEGV ||
848 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
850 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
859 void LyX::printError(ErrorItem const & ei)
861 docstring tmp = _("LyX: ") + ei.error + char_type(':')
863 std::cerr << to_utf8(tmp) << std::endl;
867 void LyX::initGuiFont()
869 if (lyxrc.roman_font_name.empty())
870 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
872 if (lyxrc.sans_font_name.empty())
873 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
875 if (lyxrc.typewriter_font_name.empty())
876 lyxrc.typewriter_font_name
877 = pimpl_->application_->typewriterFontName();
884 signal(SIGHUP, error_handler);
886 signal(SIGFPE, error_handler);
887 signal(SIGSEGV, error_handler);
888 signal(SIGINT, error_handler);
889 signal(SIGTERM, error_handler);
890 // SIGPIPE can be safely ignored.
892 lyxrc.tempdir_path = package().temp_dir().absFilename();
893 lyxrc.document_path = package().document_dir().absFilename();
895 if (lyxrc.template_path.empty()) {
896 lyxrc.template_path = addPath(package().system_support().absFilename(),
901 // Read configuration files
904 // This one may have been distributed along with LyX.
905 if (!readRcFile("lyxrc.dist"))
908 // Set the language defined by the distributor.
909 //setGuiLanguage(lyxrc.gui_language);
911 // Set the PATH correctly.
912 #if !defined (USE_POSIX_PACKAGING)
913 // Add the directory containing the LyX executable to the path
914 // so that LyX can find things like tex2lyx.
915 if (package().build_support().empty())
916 prependEnvPath("PATH", package().binary_dir().absFilename());
918 if (!lyxrc.path_prefix.empty())
919 prependEnvPath("PATH", lyxrc.path_prefix);
921 // Check that user LyX directory is ok.
922 if (queryUserLyXDir(package().explicit_user_support()))
923 reconfigureUserLyXDir();
925 // no need for a splash when there is no GUI
930 // This one is generated in user_support directory by lib/configure.py.
931 if (!readRcFile("lyxrc.defaults"))
934 // Query the OS to know what formats are viewed natively
935 formats.setAutoOpen();
937 // Read lyxrc.dist again to be able to override viewer auto-detection.
938 readRcFile("lyxrc.dist");
940 system_lyxrc = lyxrc;
941 system_formats = formats;
942 pimpl_->system_converters_ = pimpl_->converters_;
943 pimpl_->system_movers_ = pimpl_->movers_;
944 system_lcolor = lcolor;
946 // This one is edited through the preferences dialog.
947 if (!readRcFile("preferences"))
950 if (!readEncodingsFile("encodings", "unicodesymbols"))
952 if (!readLanguagesFile("languages"))
956 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
962 // read keymap and ui files in batch mode as well
963 // because InsetInfo needs to know these to produce
964 // the correct output
966 // Set the language defined by the user.
967 //setGuiLanguage(lyxrc.gui_language);
970 pimpl_->toplevel_keymap_.reset(new KeyMap);
971 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
972 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
974 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
977 if (!readUIFile(lyxrc.ui_file))
980 if (lyxerr.debugging(Debug::LYXRC))
983 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
984 if (!lyxrc.path_prefix.empty())
985 prependEnvPath("PATH", lyxrc.path_prefix);
987 FileName const document_path(lyxrc.document_path);
988 if (fs::exists(document_path.toFilesystemEncoding()) &&
989 fs::is_directory(document_path.toFilesystemEncoding()))
990 package().document_dir() = document_path;
992 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
993 if (package().temp_dir().empty()) {
994 Alert::error(_("Could not create temporary directory"),
995 bformat(_("Could not create a temporary directory in\n"
996 "%1$s. Make sure that this\n"
997 "path exists and is writable and try again."),
998 from_utf8(lyxrc.tempdir_path)));
999 // createLyXTmpDir() tries sufficiently hard to create a
1000 // usable temp dir, so the probability to come here is
1001 // close to zero. We therefore don't try to overcome this
1002 // problem with e.g. asking the user for a new path and
1003 // trying again but simply exit.
1007 LYXERR(Debug::INIT) << "LyX tmp dir: `"
1008 << package().temp_dir().absFilename()
1011 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
1012 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1014 // This must happen after package initialization and after lyxrc is
1015 // read, therefore it can't be done by a static object.
1016 ConverterCache::init();
1022 void LyX::defaultKeyBindings(KeyMap * kbmap)
1024 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
1025 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
1026 kbmap->bind("Up", FuncRequest(LFUN_UP));
1027 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
1029 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
1030 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
1031 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
1032 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
1034 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1035 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1036 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1037 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1039 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1040 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1042 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1043 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1045 // kbmap->bindings to enable the use of the numeric keypad
1046 // e.g. Num Lock set
1047 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1048 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1049 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1050 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1051 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1052 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1053 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1054 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1055 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1056 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1057 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1058 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1059 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1060 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1061 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1062 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1063 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1064 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1065 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1066 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1067 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1068 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1069 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1070 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1074 void LyX::emergencyCleanup() const
1076 // what to do about tmpfiles is non-obvious. we would
1077 // like to delete any we find, but our lyxdir might
1078 // contain documents etc. which might be helpful on
1081 pimpl_->buffer_list_.emergencyWriteAll();
1083 if (pimpl_->lyx_server_)
1084 pimpl_->lyx_server_->emergencyCleanup();
1085 pimpl_->lyx_server_.reset();
1086 pimpl_->lyx_socket_.reset();
1091 void LyX::deadKeyBindings(KeyMap * kbmap)
1093 // bindKeyings for transparent handling of deadkeys
1094 // The keysyms are gotten from XFree86 X11R6
1095 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1096 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1097 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1098 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1099 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1100 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1101 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1102 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1103 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1104 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1105 // nothing with this name
1106 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1107 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1108 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1109 // nothing with this name either...
1110 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1111 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1112 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1113 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1119 // return true if file does not exist or is older than configure.py.
1120 bool needsUpdate(string const & file)
1122 // We cannot initialize configure_script directly because the package
1123 // is not initialized yet when static objects are constructed.
1124 static string configure_script;
1125 static bool firstrun = true;
1127 configure_script = FileName(addName(
1128 package().system_support().absFilename(),
1129 "configure.py")).toFilesystemEncoding();
1133 string const absfile = FileName(addName(
1134 package().user_support().absFilename(), file)).toFilesystemEncoding();
1135 return (! fs::exists(absfile))
1136 || (fs::last_write_time(configure_script)
1137 > fs::last_write_time(absfile));
1143 bool LyX::queryUserLyXDir(bool explicit_userdir)
1145 // Does user directory exist?
1146 string const user_support =
1147 package().user_support().toFilesystemEncoding();
1148 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1149 first_start = false;
1151 return needsUpdate("lyxrc.defaults")
1152 || needsUpdate("lyxmodules.lst")
1153 || needsUpdate("textclass.lst")
1154 || needsUpdate("packages.lst");
1157 first_start = !explicit_userdir;
1159 // If the user specified explicitly a directory, ask whether
1160 // to create it. If the user says "no", then exit.
1161 if (explicit_userdir &&
1163 _("Missing user LyX directory"),
1164 bformat(_("You have specified a non-existent user "
1165 "LyX directory, %1$s.\n"
1166 "It is needed to keep your own configuration."),
1167 from_utf8(package().user_support().absFilename())),
1169 _("&Create directory"),
1171 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1172 earlyExit(EXIT_FAILURE);
1175 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1176 from_utf8(package().user_support().absFilename())))
1179 if (!createDirectory(package().user_support(), 0755)) {
1180 // Failed, so let's exit.
1181 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1183 earlyExit(EXIT_FAILURE);
1190 bool LyX::readRcFile(string const & name)
1192 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1194 FileName const lyxrc_path = libFileSearch(string(), name);
1195 if (!lyxrc_path.empty()) {
1197 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1199 if (lyxrc.read(lyxrc_path) < 0) {
1200 showFileError(name);
1204 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1210 // Read the ui file `name'
1211 bool LyX::readUIFile(string const & name, bool include)
1221 struct keyword_item uitags[ui_last - 1] = {
1222 { "include", ui_include },
1223 { "menuset", ui_menuset },
1224 { "toolbars", ui_toolbars },
1225 { "toolbarset", ui_toolbarset }
1228 // Ensure that a file is read only once (prevents include loops)
1229 static std::list<string> uifiles;
1230 std::list<string>::const_iterator it = uifiles.begin();
1231 std::list<string>::const_iterator end = uifiles.end();
1232 it = std::find(it, end, name);
1234 LYXERR(Debug::INIT) << "UI file '" << name
1235 << "' has been read already. "
1236 << "Is this an include loop?"
1241 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1246 ui_path = libFileSearch("ui", name, "inc");
1247 if (ui_path.empty())
1248 ui_path = libFileSearch("ui",
1249 changeExtension(name, "inc"));
1252 ui_path = libFileSearch("ui", name, "ui");
1254 if (ui_path.empty()) {
1255 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1256 showFileError(name);
1260 uifiles.push_back(name);
1262 LYXERR(Debug::INIT) << "Found " << name
1263 << " in " << ui_path << endl;
1264 Lexer lex(uitags, ui_last - 1);
1265 lex.setFile(ui_path);
1267 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1271 if (lyxerr.debugging(Debug::PARSER))
1272 lex.printTable(lyxerr);
1274 while (lex.isOK()) {
1275 switch (lex.lex()) {
1278 string const file = lex.getString();
1279 if (!readUIFile(file, true))
1284 menubackend.read(lex);
1288 toolbarbackend.readToolbars(lex);
1292 toolbarbackend.readToolbarSettings(lex);
1296 if (!rtrim(lex.getString()).empty())
1297 lex.printError("LyX::ReadUIFile: "
1298 "Unknown menu tag: `$$Token'");
1306 // Read the languages file `name'
1307 bool LyX::readLanguagesFile(string const & name)
1309 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1311 FileName const lang_path = libFileSearch(string(), name);
1312 if (lang_path.empty()) {
1313 showFileError(name);
1316 languages.read(lang_path);
1321 // Read the encodings file `name'
1322 bool LyX::readEncodingsFile(string const & enc_name,
1323 string const & symbols_name)
1325 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1326 << symbols_name << "..." << endl;
1328 FileName const symbols_path = libFileSearch(string(), symbols_name);
1329 if (symbols_path.empty()) {
1330 showFileError(symbols_name);
1334 FileName const enc_path = libFileSearch(string(), enc_name);
1335 if (enc_path.empty()) {
1336 showFileError(enc_name);
1339 encodings.read(enc_path, symbols_path);
1348 /// return the the number of arguments consumed
1349 typedef boost::function<int(string const &, string const &)> cmd_helper;
1351 int parse_dbg(string const & arg, string const &)
1354 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1355 Debug::showTags(lyxerr);
1358 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1360 lyxerr.level(Debug::value(arg));
1361 Debug::showLevel(lyxerr, lyxerr.level());
1366 int parse_help(string const &, string const &)
1369 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1370 "Command line switches (case sensitive):\n"
1371 "\t-help summarize LyX usage\n"
1372 "\t-userdir dir set user directory to dir\n"
1373 "\t-sysdir dir set system directory to dir\n"
1374 "\t-geometry WxH+X+Y set geometry of the main window\n"
1375 "\t-dbg feature[,feature]...\n"
1376 " select the features to debug.\n"
1377 " Type `lyx -dbg' to see the list of features\n"
1378 "\t-x [--execute] command\n"
1379 " where command is a lyx command.\n"
1380 "\t-e [--export] fmt\n"
1381 " where fmt is the export format of choice.\n"
1382 "\t-i [--import] fmt file.xxx\n"
1383 " where fmt is the import format of choice\n"
1384 " and file.xxx is the file to be imported.\n"
1385 "\t-version summarize version and build info\n"
1386 "Check the LyX man page for more details.")) << endl;
1391 int parse_version(string const &, string const &)
1393 lyxerr << "LyX " << lyx_version
1394 << " (" << lyx_release_date << ")" << endl;
1395 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1397 lyxerr << lyx_version_info << endl;
1402 int parse_sysdir(string const & arg, string const &)
1405 Alert::error(_("No system directory"),
1406 _("Missing directory for -sysdir switch"));
1409 cl_system_support = arg;
1413 int parse_userdir(string const & arg, string const &)
1416 Alert::error(_("No user directory"),
1417 _("Missing directory for -userdir switch"));
1420 cl_user_support = arg;
1424 int parse_execute(string const & arg, string const &)
1427 Alert::error(_("Incomplete command"),
1428 _("Missing command string after --execute switch"));
1435 int parse_export(string const & type, string const &)
1438 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1439 "--export switch")) << endl;
1442 batch = "buffer-export " + type;
1447 int parse_import(string const & type, string const & file)
1450 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1451 "--import switch")) << endl;
1455 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1459 batch = "buffer-import " + type + ' ' + file;
1463 int parse_geometry(string const & arg1, string const &)
1466 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1467 // remove also the arg
1470 // don't remove "-geometry"
1479 void LyX::easyParse(int & argc, char * argv[])
1481 std::map<string, cmd_helper> cmdmap;
1483 cmdmap["-dbg"] = parse_dbg;
1484 cmdmap["-help"] = parse_help;
1485 cmdmap["--help"] = parse_help;
1486 cmdmap["-version"] = parse_version;
1487 cmdmap["--version"] = parse_version;
1488 cmdmap["-sysdir"] = parse_sysdir;
1489 cmdmap["-userdir"] = parse_userdir;
1490 cmdmap["-x"] = parse_execute;
1491 cmdmap["--execute"] = parse_execute;
1492 cmdmap["-e"] = parse_export;
1493 cmdmap["--export"] = parse_export;
1494 cmdmap["-i"] = parse_import;
1495 cmdmap["--import"] = parse_import;
1496 cmdmap["-geometry"] = parse_geometry;
1498 for (int i = 1; i < argc; ++i) {
1499 std::map<string, cmd_helper>::const_iterator it
1500 = cmdmap.find(argv[i]);
1502 // don't complain if not found - may be parsed later
1503 if (it == cmdmap.end())
1506 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1507 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1509 int const remove = 1 + it->second(arg, arg2);
1511 // Now, remove used arguments by shifting
1512 // the following ones remove places down.
1515 for (int j = i; j < argc; ++j)
1516 argv[j] = argv[j + remove];
1521 batch_command = batch;
1525 FuncStatus getStatus(FuncRequest const & action)
1527 return LyX::ref().lyxFunc().getStatus(action);
1531 void dispatch(FuncRequest const & action)
1533 LyX::ref().lyxFunc().dispatch(action);
1537 BufferList & theBufferList()
1539 return LyX::ref().bufferList();
1543 LyXFunc & theLyXFunc()
1545 return LyX::ref().lyxFunc();
1549 Server & theServer()
1551 // FIXME: this should not be use_gui dependent
1552 BOOST_ASSERT(use_gui);
1553 return LyX::ref().server();
1557 ServerSocket & theServerSocket()
1559 // FIXME: this should not be use_gui dependent
1560 BOOST_ASSERT(use_gui);
1561 return LyX::ref().socket();
1565 KeyMap & theTopLevelKeymap()
1567 return LyX::ref().topLevelKeymap();
1571 Converters & theConverters()
1573 return LyX::ref().converters();
1577 Converters & theSystemConverters()
1579 return LyX::ref().systemConverters();
1583 Movers & theMovers()
1585 return LyX::ref().pimpl_->movers_;
1589 Mover const & getMover(std::string const & fmt)
1591 return LyX::ref().pimpl_->movers_(fmt);
1595 void setMover(std::string const & fmt, std::string const & command)
1597 LyX::ref().pimpl_->movers_.set(fmt, command);
1601 Movers & theSystemMovers()
1603 return LyX::ref().pimpl_->system_movers_;
1607 Messages & getMessages(std::string const & language)
1609 return LyX::ref().getMessages(language);
1613 Messages & getGuiMessages()
1615 return LyX::ref().getGuiMessages();