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::createDirectory;
93 using support::createLyXTmpDir;
94 using support::destroyDir;
95 using support::FileName;
96 using support::fileSearch;
97 using support::getEnv;
98 using support::i18nLibFileSearch;
99 using support::libFileSearch;
100 using support::package;
101 using support::prependEnvPath;
102 using support::rtrim;
103 using support::Systemcall;
104 using frontend::LyXView;
106 namespace Alert = frontend::Alert;
107 namespace os = support::os;
111 /// are we using the GUI at all?
113 * We default to true and this is changed to false when the export feature is used.
117 bool quitting; // flag, that we are quitting the program
121 // Filled with the command line arguments "foo" of "-sysdir foo" or
123 string cl_system_support;
124 string cl_user_support;
126 std::string geometryArg;
128 LyX * singleton_ = 0;
130 void showFileError(string const & error)
132 Alert::warning(_("Could not read configuration file"),
133 bformat(_("Error while reading the configuration file\n%1$s.\n"
134 "Please check your installation."), from_utf8(error)));
138 void reconfigureUserLyXDir()
140 string const configure_command = package().configure_command();
142 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
143 support::Path p(package().user_support());
145 one.startscript(Systemcall::Wait, configure_command);
146 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
152 /// The main application class private implementation.
153 struct LyX::Singletons
157 // Set the default User Interface language as soon as possible.
158 // The language used will be derived from the environment
160 messages_["GUI"] = Messages();
162 /// our function handler
165 BufferList buffer_list_;
167 boost::scoped_ptr<KeyMap> toplevel_keymap_;
169 boost::scoped_ptr<Server> lyx_server_;
171 boost::scoped_ptr<ServerSocket> lyx_socket_;
173 boost::scoped_ptr<frontend::Application> application_;
174 /// lyx session, containing lastfiles, lastfilepos, and lastopened
175 boost::scoped_ptr<Session> session_;
177 /// Files to load at start.
178 vector<FileName> files_to_load_;
180 /// The messages translators.
181 map<string, Messages> messages_;
183 /// The file converters.
184 Converters converters_;
186 // The system converters copy after reading lyxrc.defaults.
187 Converters system_converters_;
193 Movers system_movers_;
197 frontend::Application * theApp()
200 return singleton_->pimpl_->application_.get();
213 BOOST_ASSERT(singleton_);
218 LyX const & LyX::cref()
220 BOOST_ASSERT(singleton_);
229 pimpl_.reset(new Singletons);
233 BufferList & LyX::bufferList()
235 return pimpl_->buffer_list_;
239 BufferList const & LyX::bufferList() const
241 return pimpl_->buffer_list_;
245 Session & LyX::session()
247 BOOST_ASSERT(pimpl_->session_.get());
248 return *pimpl_->session_.get();
252 Session const & LyX::session() const
254 BOOST_ASSERT(pimpl_->session_.get());
255 return *pimpl_->session_.get();
259 LyXFunc & LyX::lyxFunc()
261 return pimpl_->lyxfunc_;
265 LyXFunc const & LyX::lyxFunc() const
267 return pimpl_->lyxfunc_;
271 Server & LyX::server()
273 BOOST_ASSERT(pimpl_->lyx_server_.get());
274 return *pimpl_->lyx_server_.get();
278 Server const & LyX::server() const
280 BOOST_ASSERT(pimpl_->lyx_server_.get());
281 return *pimpl_->lyx_server_.get();
285 ServerSocket & LyX::socket()
287 BOOST_ASSERT(pimpl_->lyx_socket_.get());
288 return *pimpl_->lyx_socket_.get();
292 ServerSocket const & LyX::socket() const
294 BOOST_ASSERT(pimpl_->lyx_socket_.get());
295 return *pimpl_->lyx_socket_.get();
299 frontend::Application & LyX::application()
301 BOOST_ASSERT(pimpl_->application_.get());
302 return *pimpl_->application_.get();
306 frontend::Application const & LyX::application() const
308 BOOST_ASSERT(pimpl_->application_.get());
309 return *pimpl_->application_.get();
313 KeyMap & LyX::topLevelKeymap()
315 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
316 return *pimpl_->toplevel_keymap_.get();
320 Converters & LyX::converters()
322 return pimpl_->converters_;
326 Converters & LyX::systemConverters()
328 return pimpl_->system_converters_;
332 KeyMap const & LyX::topLevelKeymap() const
334 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
335 return *pimpl_->toplevel_keymap_.get();
339 Messages & LyX::getMessages(std::string const & language)
341 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
343 if (it != pimpl_->messages_.end())
346 std::pair<map<string, Messages>::iterator, bool> result =
347 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
349 BOOST_ASSERT(result.second);
350 return result.first->second;
354 Messages & LyX::getGuiMessages()
356 return pimpl_->messages_["GUI"];
360 void LyX::setGuiLanguage(std::string const & language)
362 pimpl_->messages_["GUI"] = Messages(language);
366 Buffer const * LyX::updateInset(Inset const * inset) const
368 if (quitting || !inset)
371 Buffer const * buffer_ptr = 0;
372 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
373 vector<int>::const_iterator it = view_ids.begin();
374 vector<int>::const_iterator const end = view_ids.end();
375 for (; it != end; ++it) {
377 pimpl_->application_->gui().view(*it).updateInset(inset);
385 void LyX::hideDialogs(std::string const & name, Inset * inset) const
387 if (quitting || !use_gui)
390 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
391 vector<int>::const_iterator it = view_ids.begin();
392 vector<int>::const_iterator const end = view_ids.end();
393 for (; it != end; ++it)
394 pimpl_->application_->gui().view(*it).getDialogs().
399 int LyX::exec(int & argc, char * argv[])
401 // Here we need to parse the command line. At least
402 // we need to parse for "-dbg" and "-help"
403 easyParse(argc, argv);
406 support::init_package(to_utf8(from_local8bit(argv[0])),
407 cl_system_support, cl_user_support,
408 support::top_build_dir_is_one_level_up);
409 } catch (support::ExceptionMessage const & message) {
410 if (message.type_ == support::ErrorException) {
411 Alert::error(message.title_, message.details_);
413 } else if (message.type_ == support::WarningException) {
414 Alert::warning(message.title_, message.details_);
418 // Reinit the messages machinery in case package() knows
419 // something interesting about the locale directory.
423 // FIXME: create a ConsoleApplication
424 int exit_status = init(argc, argv);
432 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
437 BufferList::iterator begin = pimpl_->buffer_list_.begin();
439 bool final_success = false;
440 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
442 if (buf != buf->getMasterBuffer())
444 bool success = false;
445 buf->dispatch(batch_command, &success);
446 final_success |= success;
449 return !final_success;
452 // Let the frontend parse and remove all arguments that it knows
453 pimpl_->application_.reset(createApplication(argc, argv));
457 // Parse and remove all known arguments in the LyX singleton
458 // Give an error for all remaining ones.
459 int exit_status = init(argc, argv);
461 // Kill the application object before exiting.
462 pimpl_->application_.reset();
469 /* Create a CoreApplication class that will provide the main event loop
470 * and the socket callback registering. With Qt4, only QtCore
471 * library would be needed.
472 * When this is done, a server_mode could be created and the following two
473 * line would be moved out from here.
475 // Note: socket callback must be registered after init(argc, argv)
476 // such that package().temp_dir() is properly initialized.
477 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
478 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
479 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
481 // Start the real execution loop.
482 exit_status = pimpl_->application_->exec();
490 void LyX::prepareExit()
492 // Clear the clipboard and selection stack:
493 cap::clearCutStack();
494 cap::clearSelection();
496 // Set a flag that we do quitting from the program,
497 // so no refreshes are necessary.
500 // close buffers first
501 pimpl_->buffer_list_.closeAll();
503 // do any other cleanup procedures now
504 if (package().temp_dir() != package().system_temp_dir()) {
505 LYXERR(Debug::INFO) << "Deleting tmp dir "
506 << package().temp_dir().absFilename() << endl;
508 if (!destroyDir(package().temp_dir())) {
509 docstring const msg =
510 bformat(_("Unable to remove the temporary directory %1$s"),
511 from_utf8(package().temp_dir().absFilename()));
512 Alert::warning(_("Unable to remove temporary directory"), msg);
517 if (pimpl_->session_)
518 pimpl_->session_->writeFile();
519 pimpl_->session_.reset();
520 pimpl_->lyx_server_.reset();
521 pimpl_->lyx_socket_.reset();
524 // Kill the application object before exiting. This avoids crashes
525 // when exiting on Linux.
526 if (pimpl_->application_)
527 pimpl_->application_.reset();
531 void LyX::earlyExit(int status)
533 BOOST_ASSERT(pimpl_->application_.get());
534 // LyX::pimpl_::application_ is not initialised at this
535 // point so it's safe to just exit after some cleanup.
541 int LyX::init(int & argc, char * argv[])
543 // check for any spurious extra arguments
544 // other than documents
545 for (int argi = 1; argi < argc ; ++argi) {
546 if (argv[argi][0] == '-') {
548 bformat(_("Wrong command line option `%1$s'. Exiting."),
549 from_utf8(argv[argi]))) << endl;
554 // Initialization of LyX (reads lyxrc and more)
555 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
556 bool success = init();
557 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
561 for (int argi = argc - 1; argi >= 1; --argi) {
562 // get absolute path of file and add ".lyx" to
563 // the filename if necessary
564 pimpl_->files_to_load_.push_back(fileSearch(string(),
565 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
566 "lyx", support::allow_unreadable));
570 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
576 void LyX::addFileToLoad(FileName const & fname)
578 vector<FileName>::const_iterator cit = std::find(
579 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
582 if (cit == pimpl_->files_to_load_.end())
583 pimpl_->files_to_load_.push_back(fname);
587 void LyX::loadFiles()
589 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
590 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
592 for (; it != end; ++it) {
596 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
597 if (loadLyXFile(buf, *it)) {
598 ErrorList const & el = buf->errorList("Parse");
600 for_each(el.begin(), el.end(),
601 boost::bind(&LyX::printError, this, _1));
604 pimpl_->buffer_list_.release(buf);
609 void LyX::execBatchCommands()
611 // The advantage of doing this here is that the event loop
612 // is already started. So any need for interaction will be
616 // if reconfiguration is needed.
617 if (textclasslist.empty()) {
618 switch (Alert::prompt(
619 _("No textclass is found"),
620 _("LyX cannot continue because no textclass is found. "
621 "You can either reconfigure normally, or reconfigure using "
622 "default textclasses, or quit LyX."),
629 // regular reconfigure
630 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
633 // reconfigure --without-latex-config
634 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
635 " --without-latex-config"));
638 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
642 // Execute batch commands if available
643 if (batch_command.empty())
646 LYXERR(Debug::INIT) << "About to handle -x '"
647 << batch_command << '\'' << endl;
649 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
653 void LyX::restoreGuiSession()
655 LyXView * view = newLyXView();
657 // if there is no valid class list, do not load any file.
658 if (textclasslist.empty())
661 // if some files were specified at command-line we assume that the
662 // user wants to edit *these* files and not to restore the session.
663 if (!pimpl_->files_to_load_.empty()) {
664 for_each(pimpl_->files_to_load_.begin(),
665 pimpl_->files_to_load_.end(),
666 bind(&LyXView::loadLyXFile, view, _1, true));
667 // clear this list to save a few bytes of RAM
668 pimpl_->files_to_load_.clear();
669 pimpl_->session_->lastOpened().clear();
671 } else if (lyxrc.load_session) {
672 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
673 // do not add to the lastfile list since these files are restored from
674 // last session, and should be already there (regular files), or should
675 // not be added at all (help files).
676 for_each(lastopened.begin(), lastopened.end(),
677 bind(&LyXView::loadLyXFile, view, _1, false));
679 // clear this list to save a few bytes of RAM
680 pimpl_->session_->lastOpened().clear();
683 BufferList::iterator I = pimpl_->buffer_list_.begin();
684 BufferList::iterator end = pimpl_->buffer_list_.end();
685 for (; I != end; ++I) {
687 if (buf != buf->getMasterBuffer())
692 // FIXME: Switch to the last loaded Buffer. This must not be the first one
693 // because the Buffer won't be connected in this case. The correct solution
694 // would be to avoid the manual connection of the current Buffer in LyXView.
695 if (!pimpl_->buffer_list_.empty())
696 view->setBuffer(pimpl_->buffer_list_.last());
700 LyXView * LyX::newLyXView()
705 // determine windows size and position, from lyxrc and/or session
707 unsigned int width = 690;
708 unsigned int height = 510;
709 // default icon size, will be overwritten by stored session value
710 unsigned int iconSizeXY = 0;
711 int maximized = LyXView::NotMaximized;
713 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
714 width = lyxrc.geometry_width;
715 height = lyxrc.geometry_height;
717 // if lyxrc returns (0,0), then use session info
719 string val = session().sessionInfo().load("WindowWidth");
721 width = convert<unsigned int>(val);
722 val = session().sessionInfo().load("WindowHeight");
724 height = convert<unsigned int>(val);
725 val = session().sessionInfo().load("WindowMaximized");
727 maximized = convert<int>(val);
728 val = session().sessionInfo().load("IconSizeXY");
730 iconSizeXY = convert<unsigned int>(val);
733 // if user wants to restore window position
736 if (lyxrc.geometry_xysaved) {
737 string val = session().sessionInfo().load("WindowPosX");
739 posx = convert<int>(val);
740 val = session().sessionInfo().load("WindowPosY");
742 posy = convert<int>(val);
745 if (!geometryArg.empty())
751 // create the main window
752 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
760 The SIGHUP signal does not exist on Windows and does not need to be handled.
762 Windows handles SIGFPE and SIGSEGV signals as expected.
764 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
765 cause a new thread to be spawned. This may well result in unexpected
766 behaviour by the single-threaded LyX.
768 SIGTERM signals will come only from another process actually sending
769 that signal using 'raise' in Windows' POSIX compatability layer. It will
770 not come from the general "terminate process" methods that everyone
771 actually uses (and which can't be trapped). Killing an app 'politely' on
772 Windows involves first sending a WM_CLOSE message, something that is
773 caught already by the Qt frontend.
775 For more information see:
777 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
778 ...signals are mostly useless on Windows for a variety of reasons that are
781 'UNIX Application Migration Guide, Chapter 9'
782 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
784 'How To Terminate an Application "Cleanly" in Win32'
785 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
789 static void error_handler(int err_sig)
791 // Throw away any signals other than the first one received.
792 static sig_atomic_t handling_error = false;
795 handling_error = true;
797 // We have received a signal indicating a fatal error, so
798 // try and save the data ASAP.
799 LyX::cref().emergencyCleanup();
801 // These lyxerr calls may or may not work:
803 // Signals are asynchronous, so the main program may be in a very
804 // fragile state when a signal is processed and thus while a signal
805 // handler function executes.
806 // In general, therefore, we should avoid performing any
807 // I/O operations or calling most library and system functions from
810 // This shouldn't matter here, however, as we've already invoked
815 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
819 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
822 lyxerr << "\nlyx: SIGSEGV signal caught\n"
823 "Sorry, you have found a bug in LyX. "
824 "Please read the bug-reporting instructions "
825 "in Help->Introduction and send us a bug report, "
826 "if necessary. Thanks !\nBye." << endl;
834 // Deinstall the signal handlers
836 signal(SIGHUP, SIG_DFL);
838 signal(SIGINT, SIG_DFL);
839 signal(SIGFPE, SIG_DFL);
840 signal(SIGSEGV, SIG_DFL);
841 signal(SIGTERM, SIG_DFL);
844 if (err_sig == SIGSEGV ||
845 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
847 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
856 void LyX::printError(ErrorItem const & ei)
858 docstring tmp = _("LyX: ") + ei.error + char_type(':')
860 std::cerr << to_utf8(tmp) << std::endl;
864 void LyX::initGuiFont()
866 if (lyxrc.roman_font_name.empty())
867 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
869 if (lyxrc.sans_font_name.empty())
870 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
872 if (lyxrc.typewriter_font_name.empty())
873 lyxrc.typewriter_font_name
874 = pimpl_->application_->typewriterFontName();
881 signal(SIGHUP, error_handler);
883 signal(SIGFPE, error_handler);
884 signal(SIGSEGV, error_handler);
885 signal(SIGINT, error_handler);
886 signal(SIGTERM, error_handler);
887 // SIGPIPE can be safely ignored.
889 lyxrc.tempdir_path = package().temp_dir().absFilename();
890 lyxrc.document_path = package().document_dir().absFilename();
892 if (lyxrc.template_path.empty()) {
893 lyxrc.template_path = addPath(package().system_support().absFilename(),
898 // Read configuration files
901 // This one may have been distributed along with LyX.
902 if (!readRcFile("lyxrc.dist"))
905 // Set the language defined by the distributor.
906 //setGuiLanguage(lyxrc.gui_language);
908 // Set the PATH correctly.
909 #if !defined (USE_POSIX_PACKAGING)
910 // Add the directory containing the LyX executable to the path
911 // so that LyX can find things like tex2lyx.
912 if (package().build_support().empty())
913 prependEnvPath("PATH", package().binary_dir().absFilename());
915 if (!lyxrc.path_prefix.empty())
916 prependEnvPath("PATH", lyxrc.path_prefix);
918 // Check that user LyX directory is ok.
919 if (queryUserLyXDir(package().explicit_user_support()))
920 reconfigureUserLyXDir();
922 // no need for a splash when there is no GUI
927 // This one is generated in user_support directory by lib/configure.py.
928 if (!readRcFile("lyxrc.defaults"))
931 // Query the OS to know what formats are viewed natively
932 formats.setAutoOpen();
934 // Read lyxrc.dist again to be able to override viewer auto-detection.
935 readRcFile("lyxrc.dist");
937 system_lyxrc = lyxrc;
938 system_formats = formats;
939 pimpl_->system_converters_ = pimpl_->converters_;
940 pimpl_->system_movers_ = pimpl_->movers_;
941 system_lcolor = lcolor;
943 // This one is edited through the preferences dialog.
944 if (!readRcFile("preferences"))
947 if (!readEncodingsFile("encodings", "unicodesymbols"))
949 if (!readLanguagesFile("languages"))
953 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
959 // read keymap and ui files in batch mode as well
960 // because InsetInfo needs to know these to produce
961 // the correct output
963 // Set the language defined by the user.
964 //setGuiLanguage(lyxrc.gui_language);
967 pimpl_->toplevel_keymap_.reset(new KeyMap);
968 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
969 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
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 (!createDirectory(sup, 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();