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>
80 #ifndef CXX_GLOBAL_CSTD
88 using support::addName;
89 using support::addPath;
90 using support::bformat;
91 using support::changeExtension;
92 using support::createLyXTmpDir;
93 using support::FileName;
94 using support::fileSearch;
95 using support::getEnv;
96 using support::i18nLibFileSearch;
97 using support::libFileSearch;
98 using support::package;
99 using support::prependEnvPath;
100 using support::rtrim;
101 using support::Systemcall;
102 using frontend::LyXView;
104 namespace Alert = frontend::Alert;
105 namespace os = support::os;
109 /// are we using the GUI at all?
111 * We default to true and this is changed to false when the export feature is used.
115 bool quitting; // flag, that we are quitting the program
119 // Filled with the command line arguments "foo" of "-sysdir foo" or
121 string cl_system_support;
122 string cl_user_support;
124 std::string geometryArg;
126 LyX * singleton_ = 0;
128 void showFileError(string const & error)
130 Alert::warning(_("Could not read configuration file"),
131 bformat(_("Error while reading the configuration file\n%1$s.\n"
132 "Please check your installation."), from_utf8(error)));
136 void reconfigureUserLyXDir()
138 string const configure_command = package().configure_command();
140 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
141 support::Path p(package().user_support());
143 one.startscript(Systemcall::Wait, configure_command);
144 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
150 /// The main application class private implementation.
151 struct LyX::Singletons
155 // Set the default User Interface language as soon as possible.
156 // The language used will be derived from the environment
158 messages_["GUI"] = Messages();
160 /// our function handler
163 BufferList buffer_list_;
165 boost::scoped_ptr<KeyMap> toplevel_keymap_;
167 boost::scoped_ptr<Server> lyx_server_;
169 boost::scoped_ptr<ServerSocket> lyx_socket_;
171 boost::scoped_ptr<frontend::Application> application_;
172 /// lyx session, containing lastfiles, lastfilepos, and lastopened
173 boost::scoped_ptr<Session> session_;
175 /// Files to load at start.
176 vector<FileName> files_to_load_;
178 /// The messages translators.
179 map<string, Messages> messages_;
181 /// The file converters.
182 Converters converters_;
184 // The system converters copy after reading lyxrc.defaults.
185 Converters system_converters_;
191 Movers system_movers_;
195 frontend::Application * theApp()
198 return singleton_->pimpl_->application_.get();
211 BOOST_ASSERT(singleton_);
216 LyX const & LyX::cref()
218 BOOST_ASSERT(singleton_);
227 pimpl_.reset(new Singletons);
231 BufferList & LyX::bufferList()
233 return pimpl_->buffer_list_;
237 BufferList const & LyX::bufferList() const
239 return pimpl_->buffer_list_;
243 Session & LyX::session()
245 BOOST_ASSERT(pimpl_->session_.get());
246 return *pimpl_->session_.get();
250 Session const & LyX::session() const
252 BOOST_ASSERT(pimpl_->session_.get());
253 return *pimpl_->session_.get();
257 LyXFunc & LyX::lyxFunc()
259 return pimpl_->lyxfunc_;
263 LyXFunc const & LyX::lyxFunc() const
265 return pimpl_->lyxfunc_;
269 Server & LyX::server()
271 BOOST_ASSERT(pimpl_->lyx_server_.get());
272 return *pimpl_->lyx_server_.get();
276 Server const & LyX::server() const
278 BOOST_ASSERT(pimpl_->lyx_server_.get());
279 return *pimpl_->lyx_server_.get();
283 ServerSocket & LyX::socket()
285 BOOST_ASSERT(pimpl_->lyx_socket_.get());
286 return *pimpl_->lyx_socket_.get();
290 ServerSocket const & LyX::socket() const
292 BOOST_ASSERT(pimpl_->lyx_socket_.get());
293 return *pimpl_->lyx_socket_.get();
297 frontend::Application & LyX::application()
299 BOOST_ASSERT(pimpl_->application_.get());
300 return *pimpl_->application_.get();
304 frontend::Application const & LyX::application() const
306 BOOST_ASSERT(pimpl_->application_.get());
307 return *pimpl_->application_.get();
311 KeyMap & LyX::topLevelKeymap()
313 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
314 return *pimpl_->toplevel_keymap_.get();
318 Converters & LyX::converters()
320 return pimpl_->converters_;
324 Converters & LyX::systemConverters()
326 return pimpl_->system_converters_;
330 KeyMap const & LyX::topLevelKeymap() const
332 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
333 return *pimpl_->toplevel_keymap_.get();
337 Messages & LyX::getMessages(std::string const & language)
339 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
341 if (it != pimpl_->messages_.end())
344 std::pair<map<string, Messages>::iterator, bool> result =
345 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
347 BOOST_ASSERT(result.second);
348 return result.first->second;
352 Messages & LyX::getGuiMessages()
354 return pimpl_->messages_["GUI"];
358 void LyX::setGuiLanguage(std::string const & language)
360 pimpl_->messages_["GUI"] = Messages(language);
364 Buffer const * LyX::updateInset(Inset const * inset) const
366 if (quitting || !inset)
369 Buffer const * buffer_ptr = 0;
370 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
371 vector<int>::const_iterator it = view_ids.begin();
372 vector<int>::const_iterator const end = view_ids.end();
373 for (; it != end; ++it) {
375 pimpl_->application_->gui().view(*it).updateInset(inset);
383 void LyX::hideDialogs(std::string const & name, Inset * inset) const
385 if (quitting || !use_gui)
388 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
389 vector<int>::const_iterator it = view_ids.begin();
390 vector<int>::const_iterator const end = view_ids.end();
391 for (; it != end; ++it)
392 pimpl_->application_->gui().view(*it).getDialogs().
397 int LyX::exec(int & argc, char * argv[])
399 // Here we need to parse the command line. At least
400 // we need to parse for "-dbg" and "-help"
401 easyParse(argc, argv);
404 support::init_package(to_utf8(from_local8bit(argv[0])),
405 cl_system_support, cl_user_support,
406 support::top_build_dir_is_one_level_up);
407 } catch (support::ExceptionMessage const & message) {
408 if (message.type_ == support::ErrorException) {
409 Alert::error(message.title_, message.details_);
411 } else if (message.type_ == support::WarningException) {
412 Alert::warning(message.title_, message.details_);
416 // Reinit the messages machinery in case package() knows
417 // something interesting about the locale directory.
421 // FIXME: create a ConsoleApplication
422 int exit_status = init(argc, argv);
430 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
435 BufferList::iterator begin = pimpl_->buffer_list_.begin();
437 bool final_success = false;
438 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
440 if (buf != buf->masterBuffer())
442 bool success = false;
443 buf->dispatch(batch_command, &success);
444 final_success |= success;
447 return !final_success;
450 // Let the frontend parse and remove all arguments that it knows
451 pimpl_->application_.reset(createApplication(argc, argv));
455 // Parse and remove all known arguments in the LyX singleton
456 // Give an error for all remaining ones.
457 int exit_status = init(argc, argv);
459 // Kill the application object before exiting.
460 pimpl_->application_.reset();
467 /* Create a CoreApplication class that will provide the main event loop
468 * and the socket callback registering. With Qt4, only QtCore
469 * library would be needed.
470 * When this is done, a server_mode could be created and the following two
471 * line would be moved out from here.
473 // Note: socket callback must be registered after init(argc, argv)
474 // such that package().temp_dir() is properly initialized.
475 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
476 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
477 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
479 // Start the real execution loop.
480 exit_status = pimpl_->application_->exec();
488 void LyX::prepareExit()
490 // Clear the clipboard and selection stack:
491 cap::clearCutStack();
492 cap::clearSelection();
494 // Set a flag that we do quitting from the program,
495 // so no refreshes are necessary.
498 // close buffers first
499 pimpl_->buffer_list_.closeAll();
501 // do any other cleanup procedures now
502 if (package().temp_dir() != package().system_temp_dir()) {
503 LYXERR(Debug::INFO) << "Deleting tmp dir "
504 << package().temp_dir().absFilename() << endl;
506 if (!package().temp_dir().destroyDirectory()) {
507 docstring const msg =
508 bformat(_("Unable to remove the temporary directory %1$s"),
509 from_utf8(package().temp_dir().absFilename()));
510 Alert::warning(_("Unable to remove temporary directory"), msg);
515 if (pimpl_->session_)
516 pimpl_->session_->writeFile();
517 pimpl_->session_.reset();
518 pimpl_->lyx_server_.reset();
519 pimpl_->lyx_socket_.reset();
522 // Kill the application object before exiting. This avoids crashes
523 // when exiting on Linux.
524 if (pimpl_->application_)
525 pimpl_->application_.reset();
529 void LyX::earlyExit(int status)
531 BOOST_ASSERT(pimpl_->application_.get());
532 // LyX::pimpl_::application_ is not initialised at this
533 // point so it's safe to just exit after some cleanup.
539 int LyX::init(int & argc, char * argv[])
541 // check for any spurious extra arguments
542 // other than documents
543 for (int argi = 1; argi < argc ; ++argi) {
544 if (argv[argi][0] == '-') {
546 bformat(_("Wrong command line option `%1$s'. Exiting."),
547 from_utf8(argv[argi]))) << endl;
552 // Initialization of LyX (reads lyxrc and more)
553 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
554 bool success = init();
555 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
559 for (int argi = argc - 1; argi >= 1; --argi) {
560 // get absolute path of file and add ".lyx" to
561 // the filename if necessary
562 pimpl_->files_to_load_.push_back(fileSearch(string(),
563 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
564 "lyx", support::allow_unreadable));
568 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
574 void LyX::addFileToLoad(FileName const & fname)
576 vector<FileName>::const_iterator cit = std::find(
577 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
580 if (cit == pimpl_->files_to_load_.end())
581 pimpl_->files_to_load_.push_back(fname);
585 void LyX::loadFiles()
587 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
588 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
590 for (; it != end; ++it) {
594 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
595 if (loadLyXFile(buf, *it)) {
596 ErrorList const & el = buf->errorList("Parse");
598 for_each(el.begin(), el.end(),
599 boost::bind(&LyX::printError, this, _1));
602 pimpl_->buffer_list_.release(buf);
607 void LyX::execBatchCommands()
609 // The advantage of doing this here is that the event loop
610 // is already started. So any need for interaction will be
614 // if reconfiguration is needed.
615 if (textclasslist.empty()) {
616 switch (Alert::prompt(
617 _("No textclass is found"),
618 _("LyX cannot continue because no textclass is found. "
619 "You can either reconfigure normally, or reconfigure using "
620 "default textclasses, or quit LyX."),
627 // regular reconfigure
628 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
631 // reconfigure --without-latex-config
632 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
633 " --without-latex-config"));
636 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
640 // Execute batch commands if available
641 if (batch_command.empty())
644 LYXERR(Debug::INIT) << "About to handle -x '"
645 << batch_command << '\'' << endl;
647 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
651 void LyX::restoreGuiSession()
653 LyXView * view = newLyXView();
655 // if there is no valid class list, do not load any file.
656 if (textclasslist.empty())
659 // if some files were specified at command-line we assume that the
660 // user wants to edit *these* files and not to restore the session.
661 if (!pimpl_->files_to_load_.empty()) {
662 for_each(pimpl_->files_to_load_.begin(),
663 pimpl_->files_to_load_.end(),
664 bind(&LyXView::loadLyXFile, view, _1, true));
665 // clear this list to save a few bytes of RAM
666 pimpl_->files_to_load_.clear();
667 pimpl_->session_->lastOpened().clear();
669 } else if (lyxrc.load_session) {
670 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
671 // do not add to the lastfile list since these files are restored from
672 // last session, and should be already there (regular files), or should
673 // not be added at all (help files).
674 for_each(lastopened.begin(), lastopened.end(),
675 bind(&LyXView::loadLyXFile, view, _1, false));
677 // clear this list to save a few bytes of RAM
678 pimpl_->session_->lastOpened().clear();
681 BufferList::iterator I = pimpl_->buffer_list_.begin();
682 BufferList::iterator end = pimpl_->buffer_list_.end();
683 for (; I != end; ++I) {
685 if (buf != buf->masterBuffer())
690 // FIXME: Switch to the last loaded Buffer. This must not be the first one
691 // because the Buffer won't be connected in this case. The correct solution
692 // would be to avoid the manual connection of the current Buffer in LyXView.
693 if (!pimpl_->buffer_list_.empty())
694 view->setBuffer(pimpl_->buffer_list_.last());
698 LyXView * LyX::newLyXView()
703 // determine windows size and position, from lyxrc and/or session
705 unsigned int width = 690;
706 unsigned int height = 510;
707 // default icon size, will be overwritten by stored session value
708 unsigned int iconSizeXY = 0;
709 int maximized = LyXView::NotMaximized;
711 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
712 width = lyxrc.geometry_width;
713 height = lyxrc.geometry_height;
715 // if lyxrc returns (0,0), then use session info
717 string val = session().sessionInfo().load("WindowWidth");
719 width = convert<unsigned int>(val);
720 val = session().sessionInfo().load("WindowHeight");
722 height = convert<unsigned int>(val);
723 val = session().sessionInfo().load("WindowMaximized");
725 maximized = convert<int>(val);
726 val = session().sessionInfo().load("IconSizeXY");
728 iconSizeXY = convert<unsigned int>(val);
731 // if user wants to restore window position
734 if (lyxrc.geometry_xysaved) {
735 string val = session().sessionInfo().load("WindowPosX");
737 posx = convert<int>(val);
738 val = session().sessionInfo().load("WindowPosY");
740 posy = convert<int>(val);
743 if (!geometryArg.empty())
749 // create the main window
750 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
758 The SIGHUP signal does not exist on Windows and does not need to be handled.
760 Windows handles SIGFPE and SIGSEGV signals as expected.
762 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
763 cause a new thread to be spawned. This may well result in unexpected
764 behaviour by the single-threaded LyX.
766 SIGTERM signals will come only from another process actually sending
767 that signal using 'raise' in Windows' POSIX compatability layer. It will
768 not come from the general "terminate process" methods that everyone
769 actually uses (and which can't be trapped). Killing an app 'politely' on
770 Windows involves first sending a WM_CLOSE message, something that is
771 caught already by the Qt frontend.
773 For more information see:
775 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
776 ...signals are mostly useless on Windows for a variety of reasons that are
779 'UNIX Application Migration Guide, Chapter 9'
780 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
782 'How To Terminate an Application "Cleanly" in Win32'
783 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
787 static void error_handler(int err_sig)
789 // Throw away any signals other than the first one received.
790 static sig_atomic_t handling_error = false;
793 handling_error = true;
795 // We have received a signal indicating a fatal error, so
796 // try and save the data ASAP.
797 LyX::cref().emergencyCleanup();
799 // These lyxerr calls may or may not work:
801 // Signals are asynchronous, so the main program may be in a very
802 // fragile state when a signal is processed and thus while a signal
803 // handler function executes.
804 // In general, therefore, we should avoid performing any
805 // I/O operations or calling most library and system functions from
808 // This shouldn't matter here, however, as we've already invoked
813 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
817 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
820 lyxerr << "\nlyx: SIGSEGV signal caught\n"
821 "Sorry, you have found a bug in LyX. "
822 "Please read the bug-reporting instructions "
823 "in Help->Introduction and send us a bug report, "
824 "if necessary. Thanks !\nBye." << endl;
832 // Deinstall the signal handlers
834 signal(SIGHUP, SIG_DFL);
836 signal(SIGINT, SIG_DFL);
837 signal(SIGFPE, SIG_DFL);
838 signal(SIGSEGV, SIG_DFL);
839 signal(SIGTERM, SIG_DFL);
842 if (err_sig == SIGSEGV ||
843 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
845 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
854 void LyX::printError(ErrorItem const & ei)
856 docstring tmp = _("LyX: ") + ei.error + char_type(':')
858 std::cerr << to_utf8(tmp) << std::endl;
862 void LyX::initGuiFont()
864 if (lyxrc.roman_font_name.empty())
865 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
867 if (lyxrc.sans_font_name.empty())
868 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
870 if (lyxrc.typewriter_font_name.empty())
871 lyxrc.typewriter_font_name
872 = pimpl_->application_->typewriterFontName();
879 signal(SIGHUP, error_handler);
881 signal(SIGFPE, error_handler);
882 signal(SIGSEGV, error_handler);
883 signal(SIGINT, error_handler);
884 signal(SIGTERM, error_handler);
885 // SIGPIPE can be safely ignored.
887 lyxrc.tempdir_path = package().temp_dir().absFilename();
888 lyxrc.document_path = package().document_dir().absFilename();
890 if (lyxrc.template_path.empty()) {
891 lyxrc.template_path = addPath(package().system_support().absFilename(),
896 // Read configuration files
899 // This one may have been distributed along with LyX.
900 if (!readRcFile("lyxrc.dist"))
903 // Set the language defined by the distributor.
904 //setGuiLanguage(lyxrc.gui_language);
906 // Set the PATH correctly.
907 #if !defined (USE_POSIX_PACKAGING)
908 // Add the directory containing the LyX executable to the path
909 // so that LyX can find things like tex2lyx.
910 if (package().build_support().empty())
911 prependEnvPath("PATH", package().binary_dir().absFilename());
913 if (!lyxrc.path_prefix.empty())
914 prependEnvPath("PATH", lyxrc.path_prefix);
916 // Check that user LyX directory is ok.
917 if (queryUserLyXDir(package().explicit_user_support()))
918 reconfigureUserLyXDir();
920 // no need for a splash when there is no GUI
925 // This one is generated in user_support directory by lib/configure.py.
926 if (!readRcFile("lyxrc.defaults"))
929 // Query the OS to know what formats are viewed natively
930 formats.setAutoOpen();
932 // Read lyxrc.dist again to be able to override viewer auto-detection.
933 readRcFile("lyxrc.dist");
935 system_lyxrc = lyxrc;
936 system_formats = formats;
937 pimpl_->system_converters_ = pimpl_->converters_;
938 pimpl_->system_movers_ = pimpl_->movers_;
939 system_lcolor = lcolor;
941 // This one is edited through the preferences dialog.
942 if (!readRcFile("preferences"))
945 if (!readEncodingsFile("encodings", "unicodesymbols"))
947 if (!readLanguagesFile("languages"))
951 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
957 // read keymap and ui files in batch mode as well
958 // because InsetInfo needs to know these to produce
959 // the correct output
961 // Set the language defined by the user.
962 //setGuiLanguage(lyxrc.gui_language);
965 pimpl_->toplevel_keymap_.reset(new KeyMap);
966 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
967 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
968 // load user bind file user.bind
969 pimpl_->toplevel_keymap_->read("user");
971 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
974 if (!readUIFile(lyxrc.ui_file))
977 if (lyxerr.debugging(Debug::LYXRC))
980 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
981 if (!lyxrc.path_prefix.empty())
982 prependEnvPath("PATH", lyxrc.path_prefix);
984 FileName const document_path(lyxrc.document_path);
985 if (document_path.exists() && document_path.isDirectory())
986 package().document_dir() = document_path;
988 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
989 if (package().temp_dir().empty()) {
990 Alert::error(_("Could not create temporary directory"),
991 bformat(_("Could not create a temporary directory in\n"
992 "%1$s. Make sure that this\n"
993 "path exists and is writable and try again."),
994 from_utf8(lyxrc.tempdir_path)));
995 // createLyXTmpDir() tries sufficiently hard to create a
996 // usable temp dir, so the probability to come here is
997 // close to zero. We therefore don't try to overcome this
998 // problem with e.g. asking the user for a new path and
999 // trying again but simply exit.
1003 LYXERR(Debug::INIT) << "LyX tmp dir: `"
1004 << package().temp_dir().absFilename()
1007 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
1008 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1010 // This must happen after package initialization and after lyxrc is
1011 // read, therefore it can't be done by a static object.
1012 ConverterCache::init();
1018 void LyX::defaultKeyBindings(KeyMap * kbmap)
1020 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
1021 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
1022 kbmap->bind("Up", FuncRequest(LFUN_UP));
1023 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
1025 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
1026 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
1027 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
1028 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
1030 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1031 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1032 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1033 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1035 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1036 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1038 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1039 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1041 // kbmap->bindings to enable the use of the numeric keypad
1042 // e.g. Num Lock set
1043 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1044 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1045 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1046 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1047 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1048 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1049 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1050 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1051 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1052 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1053 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1054 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1055 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1056 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1057 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1058 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1059 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1060 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1061 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1062 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1063 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1064 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1065 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1066 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1070 void LyX::emergencyCleanup() const
1072 // what to do about tmpfiles is non-obvious. we would
1073 // like to delete any we find, but our lyxdir might
1074 // contain documents etc. which might be helpful on
1077 pimpl_->buffer_list_.emergencyWriteAll();
1079 if (pimpl_->lyx_server_)
1080 pimpl_->lyx_server_->emergencyCleanup();
1081 pimpl_->lyx_server_.reset();
1082 pimpl_->lyx_socket_.reset();
1087 void LyX::deadKeyBindings(KeyMap * kbmap)
1089 // bindKeyings for transparent handling of deadkeys
1090 // The keysyms are gotten from XFree86 X11R6
1091 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1092 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1093 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1094 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1095 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1096 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1097 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1098 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1099 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1100 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1101 // nothing with this name
1102 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1103 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1104 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1105 // nothing with this name either...
1106 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1107 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1108 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1109 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1113 // return true if file does not exist or is older than configure.py.
1114 static bool needsUpdate(string const & file)
1116 // We cannot initialize configure_script directly because the package
1117 // is not initialized yet when static objects are constructed.
1118 static FileName configure_script;
1119 static bool firstrun = true;
1122 FileName(addName(package().system_support().absFilename(),
1128 FileName(addName(package().user_support().absFilename(), file));
1129 return !absfile.exists()
1130 || configure_script.lastModified() > absfile.lastModified();
1134 bool LyX::queryUserLyXDir(bool explicit_userdir)
1136 // Does user directory exist?
1137 FileName const sup = package().user_support();
1138 if (sup.exists() && sup.isDirectory()) {
1139 first_start = false;
1141 return needsUpdate("lyxrc.defaults")
1142 || needsUpdate("lyxmodules.lst")
1143 || needsUpdate("textclass.lst")
1144 || needsUpdate("packages.lst");
1147 first_start = !explicit_userdir;
1149 // If the user specified explicitly a directory, ask whether
1150 // to create it. If the user says "no", then exit.
1151 if (explicit_userdir &&
1153 _("Missing user LyX directory"),
1154 bformat(_("You have specified a non-existent user "
1155 "LyX directory, %1$s.\n"
1156 "It is needed to keep your own configuration."),
1157 from_utf8(package().user_support().absFilename())),
1159 _("&Create directory"),
1161 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1162 earlyExit(EXIT_FAILURE);
1165 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1166 from_utf8(sup.absFilename()))) << endl;
1168 if (!sup.createDirectory(0755)) {
1169 // Failed, so let's exit.
1170 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1172 earlyExit(EXIT_FAILURE);
1179 bool LyX::readRcFile(string const & name)
1181 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1183 FileName const lyxrc_path = libFileSearch(string(), name);
1184 if (!lyxrc_path.empty()) {
1186 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1188 if (lyxrc.read(lyxrc_path) < 0) {
1189 showFileError(name);
1193 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1199 // Read the ui file `name'
1200 bool LyX::readUIFile(string const & name, bool include)
1210 struct keyword_item uitags[ui_last - 1] = {
1211 { "include", ui_include },
1212 { "menuset", ui_menuset },
1213 { "toolbars", ui_toolbars },
1214 { "toolbarset", ui_toolbarset }
1217 // Ensure that a file is read only once (prevents include loops)
1218 static std::list<string> uifiles;
1219 std::list<string>::const_iterator it = uifiles.begin();
1220 std::list<string>::const_iterator end = uifiles.end();
1221 it = std::find(it, end, name);
1223 LYXERR(Debug::INIT) << "UI file '" << name
1224 << "' has been read already. "
1225 << "Is this an include loop?"
1230 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1235 ui_path = libFileSearch("ui", name, "inc");
1236 if (ui_path.empty())
1237 ui_path = libFileSearch("ui",
1238 changeExtension(name, "inc"));
1241 ui_path = libFileSearch("ui", name, "ui");
1243 if (ui_path.empty()) {
1244 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1245 showFileError(name);
1249 uifiles.push_back(name);
1251 LYXERR(Debug::INIT) << "Found " << name
1252 << " in " << ui_path << endl;
1253 Lexer lex(uitags, ui_last - 1);
1254 lex.setFile(ui_path);
1256 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1260 if (lyxerr.debugging(Debug::PARSER))
1261 lex.printTable(lyxerr);
1263 while (lex.isOK()) {
1264 switch (lex.lex()) {
1267 string const file = lex.getString();
1268 if (!readUIFile(file, true))
1273 menubackend.read(lex);
1277 toolbarbackend.readToolbars(lex);
1281 toolbarbackend.readToolbarSettings(lex);
1285 if (!rtrim(lex.getString()).empty())
1286 lex.printError("LyX::ReadUIFile: "
1287 "Unknown menu tag: `$$Token'");
1295 // Read the languages file `name'
1296 bool LyX::readLanguagesFile(string const & name)
1298 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1300 FileName const lang_path = libFileSearch(string(), name);
1301 if (lang_path.empty()) {
1302 showFileError(name);
1305 languages.read(lang_path);
1310 // Read the encodings file `name'
1311 bool LyX::readEncodingsFile(string const & enc_name,
1312 string const & symbols_name)
1314 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1315 << symbols_name << "..." << endl;
1317 FileName const symbols_path = libFileSearch(string(), symbols_name);
1318 if (symbols_path.empty()) {
1319 showFileError(symbols_name);
1323 FileName const enc_path = libFileSearch(string(), enc_name);
1324 if (enc_path.empty()) {
1325 showFileError(enc_name);
1328 encodings.read(enc_path, symbols_path);
1337 /// return the the number of arguments consumed
1338 typedef boost::function<int(string const &, string const &)> cmd_helper;
1340 int parse_dbg(string const & arg, string const &)
1343 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1344 Debug::showTags(lyxerr);
1347 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1349 lyxerr.level(Debug::value(arg));
1350 Debug::showLevel(lyxerr, lyxerr.level());
1355 int parse_help(string const &, string const &)
1358 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1359 "Command line switches (case sensitive):\n"
1360 "\t-help summarize LyX usage\n"
1361 "\t-userdir dir set user directory to dir\n"
1362 "\t-sysdir dir set system directory to dir\n"
1363 "\t-geometry WxH+X+Y set geometry of the main window\n"
1364 "\t-dbg feature[,feature]...\n"
1365 " select the features to debug.\n"
1366 " Type `lyx -dbg' to see the list of features\n"
1367 "\t-x [--execute] command\n"
1368 " where command is a lyx command.\n"
1369 "\t-e [--export] fmt\n"
1370 " where fmt is the export format of choice.\n"
1371 "\t-i [--import] fmt file.xxx\n"
1372 " where fmt is the import format of choice\n"
1373 " and file.xxx is the file to be imported.\n"
1374 "\t-version summarize version and build info\n"
1375 "Check the LyX man page for more details.")) << endl;
1380 int parse_version(string const &, string const &)
1382 lyxerr << "LyX " << lyx_version
1383 << " (" << lyx_release_date << ")" << endl;
1384 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1386 lyxerr << lyx_version_info << endl;
1391 int parse_sysdir(string const & arg, string const &)
1394 Alert::error(_("No system directory"),
1395 _("Missing directory for -sysdir switch"));
1398 cl_system_support = arg;
1402 int parse_userdir(string const & arg, string const &)
1405 Alert::error(_("No user directory"),
1406 _("Missing directory for -userdir switch"));
1409 cl_user_support = arg;
1413 int parse_execute(string const & arg, string const &)
1416 Alert::error(_("Incomplete command"),
1417 _("Missing command string after --execute switch"));
1424 int parse_export(string const & type, string const &)
1427 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1428 "--export switch")) << endl;
1431 batch = "buffer-export " + type;
1436 int parse_import(string const & type, string const & file)
1439 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1440 "--import switch")) << endl;
1444 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1448 batch = "buffer-import " + type + ' ' + file;
1452 int parse_geometry(string const & arg1, string const &)
1455 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1456 // remove also the arg
1459 // don't remove "-geometry"
1468 void LyX::easyParse(int & argc, char * argv[])
1470 std::map<string, cmd_helper> cmdmap;
1472 cmdmap["-dbg"] = parse_dbg;
1473 cmdmap["-help"] = parse_help;
1474 cmdmap["--help"] = parse_help;
1475 cmdmap["-version"] = parse_version;
1476 cmdmap["--version"] = parse_version;
1477 cmdmap["-sysdir"] = parse_sysdir;
1478 cmdmap["-userdir"] = parse_userdir;
1479 cmdmap["-x"] = parse_execute;
1480 cmdmap["--execute"] = parse_execute;
1481 cmdmap["-e"] = parse_export;
1482 cmdmap["--export"] = parse_export;
1483 cmdmap["-i"] = parse_import;
1484 cmdmap["--import"] = parse_import;
1485 cmdmap["-geometry"] = parse_geometry;
1487 for (int i = 1; i < argc; ++i) {
1488 std::map<string, cmd_helper>::const_iterator it
1489 = cmdmap.find(argv[i]);
1491 // don't complain if not found - may be parsed later
1492 if (it == cmdmap.end())
1495 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1496 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1498 int const remove = 1 + it->second(arg, arg2);
1500 // Now, remove used arguments by shifting
1501 // the following ones remove places down.
1504 for (int j = i; j < argc; ++j)
1505 argv[j] = argv[j + remove];
1510 batch_command = batch;
1514 FuncStatus getStatus(FuncRequest const & action)
1516 return LyX::ref().lyxFunc().getStatus(action);
1520 void dispatch(FuncRequest const & action)
1522 LyX::ref().lyxFunc().dispatch(action);
1526 BufferList & theBufferList()
1528 return LyX::ref().bufferList();
1532 LyXFunc & theLyXFunc()
1534 return LyX::ref().lyxFunc();
1538 Server & theServer()
1540 // FIXME: this should not be use_gui dependent
1541 BOOST_ASSERT(use_gui);
1542 return LyX::ref().server();
1546 ServerSocket & theServerSocket()
1548 // FIXME: this should not be use_gui dependent
1549 BOOST_ASSERT(use_gui);
1550 return LyX::ref().socket();
1554 KeyMap & theTopLevelKeymap()
1556 return LyX::ref().topLevelKeymap();
1560 Converters & theConverters()
1562 return LyX::ref().converters();
1566 Converters & theSystemConverters()
1568 return LyX::ref().systemConverters();
1572 Movers & theMovers()
1574 return LyX::ref().pimpl_->movers_;
1578 Mover const & getMover(std::string const & fmt)
1580 return LyX::ref().pimpl_->movers_(fmt);
1584 void setMover(std::string const & fmt, std::string const & command)
1586 LyX::ref().pimpl_->movers_.set(fmt, command);
1590 Movers & theSystemMovers()
1592 return LyX::ref().pimpl_->system_movers_;
1596 Messages & getMessages(std::string const & language)
1598 return LyX::ref().getMessages(language);
1602 Messages & getGuiMessages()
1604 return LyX::ref().getGuiMessages();