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"
36 #include "LyXAction.h"
40 #include "ModuleList.h"
42 #include "ServerSocket.h"
43 #include "TextClassList.h"
44 #include "MenuBackend.h"
47 #include "ToolbarBackend.h"
49 #include "frontends/alert.h"
50 #include "frontends/Application.h"
51 #include "frontends/Dialogs.h"
52 #include "frontends/Gui.h"
53 #include "frontends/LyXView.h"
55 #include "support/environment.h"
56 #include "support/filetools.h"
57 #include "support/lyxlib.h"
58 #include "support/convert.h"
59 #include "support/ExceptionMessage.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62 #include "support/Path.h"
63 #include "support/Systemcall.h"
65 #include <boost/bind.hpp>
66 #include <boost/filesystem/operations.hpp>
82 #ifndef CXX_GLOBAL_CSTD
88 namespace fs = boost::filesystem;
92 using support::addName;
93 using support::addPath;
94 using support::bformat;
95 using support::changeExtension;
96 using support::createDirectory;
97 using support::createLyXTmpDir;
98 using support::destroyDir;
99 using support::FileName;
100 using support::fileSearch;
101 using support::getEnv;
102 using support::i18nLibFileSearch;
103 using support::libFileSearch;
104 using support::package;
105 using support::prependEnvPath;
106 using support::rtrim;
107 using support::Systemcall;
108 using frontend::LyXView;
110 namespace Alert = frontend::Alert;
111 namespace os = support::os;
115 /// are we using the GUI at all?
117 * We default to true and this is changed to false when the export feature is used.
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);
408 try { support::init_package(to_utf8(from_local8bit(argv[0])),
409 cl_system_support, cl_user_support,
410 support::top_build_dir_is_one_level_up);
411 } catch (support::ExceptionMessage const & message) {
412 if (message.type_ == support::ErrorException) {
413 Alert::error(message.title_, message.details_);
415 } else if (message.type_ == support::WarningException) {
416 Alert::warning(message.title_, message.details_);
420 // Reinit the messages machinery in case package() knows
421 // something interesting about the locale directory.
425 // FIXME: create a ConsoleApplication
426 int exit_status = init(argc, argv);
434 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
439 BufferList::iterator begin = pimpl_->buffer_list_.begin();
441 bool final_success = false;
442 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
444 if (buf != buf->getMasterBuffer())
446 bool success = false;
447 buf->dispatch(batch_command, &success);
448 final_success |= success;
451 return !final_success;
454 // Let the frontend parse and remove all arguments that it knows
455 pimpl_->application_.reset(createApplication(argc, argv));
459 // Parse and remove all known arguments in the LyX singleton
460 // Give an error for all remaining ones.
461 int exit_status = init(argc, argv);
463 // Kill the application object before exiting.
464 pimpl_->application_.reset();
471 /* Create a CoreApplication class that will provide the main event loop
472 * and the socket callback registering. With Qt4, only QtCore
473 * library would be needed.
474 * When this is done, a server_mode could be created and the following two
475 * line would be moved out from here.
477 // Note: socket callback must be registered after init(argc, argv)
478 // such that package().temp_dir() is properly initialized.
479 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
480 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
481 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
483 // Start the real execution loop.
484 exit_status = pimpl_->application_->exec();
492 void LyX::prepareExit()
494 // Clear the clipboard and selection stack:
495 cap::clearCutStack();
496 cap::clearSelection();
498 // Set a flag that we do quitting from the program,
499 // so no refreshes are necessary.
502 // close buffers first
503 pimpl_->buffer_list_.closeAll();
505 // do any other cleanup procedures now
506 if (package().temp_dir() != package().system_temp_dir()) {
507 LYXERR(Debug::INFO) << "Deleting tmp dir "
508 << package().temp_dir().absFilename() << endl;
510 if (!destroyDir(package().temp_dir())) {
511 docstring const msg =
512 bformat(_("Unable to remove the temporary directory %1$s"),
513 from_utf8(package().temp_dir().absFilename()));
514 Alert::warning(_("Unable to remove temporary directory"), msg);
519 if (pimpl_->session_)
520 pimpl_->session_->writeFile();
521 pimpl_->session_.reset();
522 pimpl_->lyx_server_.reset();
523 pimpl_->lyx_socket_.reset();
526 // Kill the application object before exiting. This avoids crashes
527 // when exiting on Linux.
528 if (pimpl_->application_)
529 pimpl_->application_.reset();
533 void LyX::earlyExit(int status)
535 BOOST_ASSERT(pimpl_->application_.get());
536 // LyX::pimpl_::application_ is not initialised at this
537 // point so it's safe to just exit after some cleanup.
543 int LyX::init(int & argc, char * argv[])
545 // check for any spurious extra arguments
546 // other than documents
547 for (int argi = 1; argi < argc ; ++argi) {
548 if (argv[argi][0] == '-') {
550 bformat(_("Wrong command line option `%1$s'. Exiting."),
551 from_utf8(argv[argi]))) << endl;
556 // Initialization of LyX (reads lyxrc and more)
557 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
558 bool success = init();
559 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
563 for (int argi = argc - 1; argi >= 1; --argi) {
564 // get absolute path of file and add ".lyx" to
565 // the filename if necessary
566 pimpl_->files_to_load_.push_back(fileSearch(string(),
567 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
568 "lyx", support::allow_unreadable));
572 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
578 void LyX::addFileToLoad(FileName const & fname)
580 vector<FileName>::const_iterator cit = std::find(
581 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
584 if (cit == pimpl_->files_to_load_.end())
585 pimpl_->files_to_load_.push_back(fname);
589 void LyX::loadFiles()
591 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
592 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
594 for (; it != end; ++it) {
598 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
599 if (loadLyXFile(buf, *it)) {
600 ErrorList const & el = buf->errorList("Parse");
602 for_each(el.begin(), el.end(),
603 boost::bind(&LyX::printError, this, _1));
606 pimpl_->buffer_list_.release(buf);
611 void LyX::execBatchCommands()
613 // The advantage of doing this here is that the event loop
614 // is already started. So any need for interaction will be
618 // if reconfiguration is needed.
619 if (textclasslist.empty()) {
620 switch (Alert::prompt(
621 _("No textclass is found"),
622 _("LyX cannot continue because no textclass is found. "
623 "You can either reconfigure normally, or reconfigure using "
624 "default textclasses, or quit LyX."),
631 // regular reconfigure
632 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
635 // reconfigure --without-latex-config
636 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
637 " --without-latex-config"));
640 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
644 // Execute batch commands if available
645 if (batch_command.empty())
648 LYXERR(Debug::INIT) << "About to handle -x '"
649 << batch_command << '\'' << endl;
651 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
655 void LyX::restoreGuiSession()
657 LyXView * view = newLyXView();
659 // if there is no valid class list, do not load any file.
660 if (textclasslist.empty())
663 // if some files were specified at command-line we assume that the
664 // user wants to edit *these* files and not to restore the session.
665 if (!pimpl_->files_to_load_.empty()) {
666 for_each(pimpl_->files_to_load_.begin(),
667 pimpl_->files_to_load_.end(),
668 bind(&LyXView::loadLyXFile, view, _1, true));
669 // clear this list to save a few bytes of RAM
670 pimpl_->files_to_load_.clear();
671 pimpl_->session_->lastOpened().clear();
673 } else if (lyxrc.load_session) {
674 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
675 // do not add to the lastfile list since these files are restored from
676 // last session, and should be already there (regular files), or should
677 // not be added at all (help files).
678 for_each(lastopened.begin(), lastopened.end(),
679 bind(&LyXView::loadLyXFile, view, _1, false));
681 // clear this list to save a few bytes of RAM
682 pimpl_->session_->lastOpened().clear();
685 BufferList::iterator I = pimpl_->buffer_list_.begin();
686 BufferList::iterator end = pimpl_->buffer_list_.end();
687 for (; I != end; ++I) {
689 if (buf != buf->getMasterBuffer())
694 // FIXME: Switch to the last loaded Buffer. This must not be the first one
695 // because the Buffer won't be connected in this case. The correct solution
696 // would be to avoid the manual connection of the current Buffer in LyXView.
697 if (!pimpl_->buffer_list_.empty())
698 view->setBuffer(pimpl_->buffer_list_.last());
702 LyXView * LyX::newLyXView()
707 // determine windows size and position, from lyxrc and/or session
709 unsigned int width = 690;
710 unsigned int height = 510;
711 // default icon size, will be overwritten by stored session value
712 unsigned int iconSizeXY = 0;
713 int maximized = LyXView::NotMaximized;
715 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
716 width = lyxrc.geometry_width;
717 height = lyxrc.geometry_height;
719 // if lyxrc returns (0,0), then use session info
721 string val = session().sessionInfo().load("WindowWidth");
723 width = convert<unsigned int>(val);
724 val = session().sessionInfo().load("WindowHeight");
726 height = convert<unsigned int>(val);
727 val = session().sessionInfo().load("WindowMaximized");
729 maximized = convert<int>(val);
730 val = session().sessionInfo().load("IconSizeXY");
732 iconSizeXY = convert<unsigned int>(val);
735 // if user wants to restore window position
738 if (lyxrc.geometry_xysaved) {
739 string val = session().sessionInfo().load("WindowPosX");
741 posx = convert<int>(val);
742 val = session().sessionInfo().load("WindowPosY");
744 posy = convert<int>(val);
747 if (!geometryArg.empty())
753 // create the main window
754 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
762 The SIGHUP signal does not exist on Windows and does not need to be handled.
764 Windows handles SIGFPE and SIGSEGV signals as expected.
766 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
767 cause a new thread to be spawned. This may well result in unexpected
768 behaviour by the single-threaded LyX.
770 SIGTERM signals will come only from another process actually sending
771 that signal using 'raise' in Windows' POSIX compatability layer. It will
772 not come from the general "terminate process" methods that everyone
773 actually uses (and which can't be trapped). Killing an app 'politely' on
774 Windows involves first sending a WM_CLOSE message, something that is
775 caught already by the Qt frontend.
777 For more information see:
779 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
780 ...signals are mostly useless on Windows for a variety of reasons that are
783 'UNIX Application Migration Guide, Chapter 9'
784 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
786 'How To Terminate an Application "Cleanly" in Win32'
787 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
791 static void error_handler(int err_sig)
793 // Throw away any signals other than the first one received.
794 static sig_atomic_t handling_error = false;
797 handling_error = true;
799 // We have received a signal indicating a fatal error, so
800 // try and save the data ASAP.
801 LyX::cref().emergencyCleanup();
803 // These lyxerr calls may or may not work:
805 // Signals are asynchronous, so the main program may be in a very
806 // fragile state when a signal is processed and thus while a signal
807 // handler function executes.
808 // In general, therefore, we should avoid performing any
809 // I/O operations or calling most library and system functions from
812 // This shouldn't matter here, however, as we've already invoked
817 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
821 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
824 lyxerr << "\nlyx: SIGSEGV signal caught\n"
825 "Sorry, you have found a bug in LyX. "
826 "Please read the bug-reporting instructions "
827 "in Help->Introduction and send us a bug report, "
828 "if necessary. Thanks !\nBye." << endl;
836 // Deinstall the signal handlers
838 signal(SIGHUP, SIG_DFL);
840 signal(SIGINT, SIG_DFL);
841 signal(SIGFPE, SIG_DFL);
842 signal(SIGSEGV, SIG_DFL);
843 signal(SIGTERM, SIG_DFL);
846 if (err_sig == SIGSEGV ||
847 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
849 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
858 void LyX::printError(ErrorItem const & ei)
860 docstring tmp = _("LyX: ") + ei.error + char_type(':')
862 std::cerr << to_utf8(tmp) << std::endl;
866 void LyX::initGuiFont()
868 if (lyxrc.roman_font_name.empty())
869 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
871 if (lyxrc.sans_font_name.empty())
872 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
874 if (lyxrc.typewriter_font_name.empty())
875 lyxrc.typewriter_font_name
876 = pimpl_->application_->typewriterFontName();
883 signal(SIGHUP, error_handler);
885 signal(SIGFPE, error_handler);
886 signal(SIGSEGV, error_handler);
887 signal(SIGINT, error_handler);
888 signal(SIGTERM, error_handler);
889 // SIGPIPE can be safely ignored.
891 lyxrc.tempdir_path = package().temp_dir().absFilename();
892 lyxrc.document_path = package().document_dir().absFilename();
894 if (lyxrc.template_path.empty()) {
895 lyxrc.template_path = addPath(package().system_support().absFilename(),
900 // Read configuration files
903 // This one may have been distributed along with LyX.
904 if (!readRcFile("lyxrc.dist"))
907 // Set the language defined by the distributor.
908 //setGuiLanguage(lyxrc.gui_language);
910 // Set the PATH correctly.
911 #if !defined (USE_POSIX_PACKAGING)
912 // Add the directory containing the LyX executable to the path
913 // so that LyX can find things like tex2lyx.
914 if (package().build_support().empty())
915 prependEnvPath("PATH", package().binary_dir().absFilename());
917 if (!lyxrc.path_prefix.empty())
918 prependEnvPath("PATH", lyxrc.path_prefix);
920 // Check that user LyX directory is ok.
921 if (queryUserLyXDir(package().explicit_user_support()))
922 reconfigureUserLyXDir();
924 // no need for a splash when there is no GUI
929 // This one is generated in user_support directory by lib/configure.py.
930 if (!readRcFile("lyxrc.defaults"))
933 // Query the OS to know what formats are viewed natively
934 formats.setAutoOpen();
936 // Read lyxrc.dist again to be able to override viewer auto-detection.
937 readRcFile("lyxrc.dist");
939 system_lyxrc = lyxrc;
940 system_formats = formats;
941 pimpl_->system_converters_ = pimpl_->converters_;
942 pimpl_->system_movers_ = pimpl_->movers_;
943 system_lcolor = lcolor;
945 // This one is edited through the preferences dialog.
946 if (!readRcFile("preferences"))
949 if (!readEncodingsFile("encodings", "unicodesymbols"))
951 if (!readLanguagesFile("languages"))
955 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
962 // Set the language defined by the user.
963 //setGuiLanguage(lyxrc.gui_language);
966 pimpl_->toplevel_keymap_.reset(new KeyMap);
967 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
968 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
970 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
973 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 (fs::exists(document_path.toFilesystemEncoding()) &&
986 fs::is_directory(document_path.toFilesystemEncoding()))
987 package().document_dir() = document_path;
989 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
990 if (package().temp_dir().empty()) {
991 Alert::error(_("Could not create temporary directory"),
992 bformat(_("Could not create a temporary directory in\n"
993 "%1$s. Make sure that this\n"
994 "path exists and is writable and try again."),
995 from_utf8(lyxrc.tempdir_path)));
996 // createLyXTmpDir() tries sufficiently hard to create a
997 // usable temp dir, so the probability to come here is
998 // close to zero. We therefore don't try to overcome this
999 // problem with e.g. asking the user for a new path and
1000 // trying again but simply exit.
1004 LYXERR(Debug::INIT) << "LyX tmp dir: `"
1005 << package().temp_dir().absFilename()
1008 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
1009 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1011 // This must happen after package initialization and after lyxrc is
1012 // read, therefore it can't be done by a static object.
1013 ConverterCache::init();
1019 void LyX::defaultKeyBindings(KeyMap * kbmap)
1021 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
1022 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
1023 kbmap->bind("Up", FuncRequest(LFUN_UP));
1024 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
1026 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
1027 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
1028 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
1029 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
1031 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1032 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1033 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1034 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1036 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1037 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1039 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1040 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1042 // kbmap->bindings to enable the use of the numeric keypad
1043 // e.g. Num Lock set
1044 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1045 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1046 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1047 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1048 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1049 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1050 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1051 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1052 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1053 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1054 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1055 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1056 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1057 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1058 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1059 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1060 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1061 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1062 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1063 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1064 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1065 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1066 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1067 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1071 void LyX::emergencyCleanup() const
1073 // what to do about tmpfiles is non-obvious. we would
1074 // like to delete any we find, but our lyxdir might
1075 // contain documents etc. which might be helpful on
1078 pimpl_->buffer_list_.emergencyWriteAll();
1080 if (pimpl_->lyx_server_)
1081 pimpl_->lyx_server_->emergencyCleanup();
1082 pimpl_->lyx_server_.reset();
1083 pimpl_->lyx_socket_.reset();
1088 void LyX::deadKeyBindings(KeyMap * kbmap)
1090 // bindKeyings for transparent handling of deadkeys
1091 // The keysyms are gotten from XFree86 X11R6
1092 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1093 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1094 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1095 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1096 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1097 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1098 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1099 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1100 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1101 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1102 // nothing with this name
1103 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1104 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1105 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1106 // nothing with this name either...
1107 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1108 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1109 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1110 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1116 // return true if file does not exist or is older than configure.py.
1117 bool needsUpdate(string const & file)
1119 // We cannot initialize configure_script directly because the package
1120 // is not initialized yet when static objects are constructed.
1121 static string configure_script;
1122 static bool firstrun = true;
1124 configure_script = FileName(addName(
1125 package().system_support().absFilename(),
1126 "configure.py")).toFilesystemEncoding();
1130 string const absfile = FileName(addName(
1131 package().user_support().absFilename(), file)).toFilesystemEncoding();
1132 return (! fs::exists(absfile))
1133 || (fs::last_write_time(configure_script)
1134 > fs::last_write_time(absfile));
1140 bool LyX::queryUserLyXDir(bool explicit_userdir)
1142 // Does user directory exist?
1143 string const user_support =
1144 package().user_support().toFilesystemEncoding();
1145 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1146 first_start = false;
1148 return needsUpdate("lyxrc.defaults")
1149 || needsUpdate("lyxmodules.lst")
1150 || needsUpdate("textclass.lst")
1151 || needsUpdate("packages.lst");
1154 first_start = !explicit_userdir;
1156 // If the user specified explicitly a directory, ask whether
1157 // to create it. If the user says "no", then exit.
1158 if (explicit_userdir &&
1160 _("Missing user LyX directory"),
1161 bformat(_("You have specified a non-existent user "
1162 "LyX directory, %1$s.\n"
1163 "It is needed to keep your own configuration."),
1164 from_utf8(package().user_support().absFilename())),
1166 _("&Create directory"),
1168 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1169 earlyExit(EXIT_FAILURE);
1172 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1173 from_utf8(package().user_support().absFilename())))
1176 if (!createDirectory(package().user_support(), 0755)) {
1177 // Failed, so let's exit.
1178 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1180 earlyExit(EXIT_FAILURE);
1187 bool LyX::readRcFile(string const & name)
1189 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1191 FileName const lyxrc_path = libFileSearch(string(), name);
1192 if (!lyxrc_path.empty()) {
1194 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1196 if (lyxrc.read(lyxrc_path) < 0) {
1197 showFileError(name);
1201 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1207 // Read the ui file `name'
1208 bool LyX::readUIFile(string const & name, bool include)
1218 struct keyword_item uitags[ui_last - 1] = {
1219 { "include", ui_include },
1220 { "menuset", ui_menuset },
1221 { "toolbars", ui_toolbars },
1222 { "toolbarset", ui_toolbarset }
1225 // Ensure that a file is read only once (prevents include loops)
1226 static std::list<string> uifiles;
1227 std::list<string>::const_iterator it = uifiles.begin();
1228 std::list<string>::const_iterator end = uifiles.end();
1229 it = std::find(it, end, name);
1231 LYXERR(Debug::INIT) << "UI file '" << name
1232 << "' has been read already. "
1233 << "Is this an include loop?"
1238 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1243 ui_path = libFileSearch("ui", name, "inc");
1244 if (ui_path.empty())
1245 ui_path = libFileSearch("ui",
1246 changeExtension(name, "inc"));
1249 ui_path = libFileSearch("ui", name, "ui");
1251 if (ui_path.empty()) {
1252 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1253 showFileError(name);
1257 uifiles.push_back(name);
1259 LYXERR(Debug::INIT) << "Found " << name
1260 << " in " << ui_path << endl;
1261 Lexer lex(uitags, ui_last - 1);
1262 lex.setFile(ui_path);
1264 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1268 if (lyxerr.debugging(Debug::PARSER))
1269 lex.printTable(lyxerr);
1271 while (lex.isOK()) {
1272 switch (lex.lex()) {
1275 string const file = lex.getString();
1276 if (!readUIFile(file, true))
1281 menubackend.read(lex);
1285 toolbarbackend.readToolbars(lex);
1289 toolbarbackend.readToolbarSettings(lex);
1293 if (!rtrim(lex.getString()).empty())
1294 lex.printError("LyX::ReadUIFile: "
1295 "Unknown menu tag: `$$Token'");
1303 // Read the languages file `name'
1304 bool LyX::readLanguagesFile(string const & name)
1306 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1308 FileName const lang_path = libFileSearch(string(), name);
1309 if (lang_path.empty()) {
1310 showFileError(name);
1313 languages.read(lang_path);
1318 // Read the encodings file `name'
1319 bool LyX::readEncodingsFile(string const & enc_name,
1320 string const & symbols_name)
1322 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1323 << symbols_name << "..." << endl;
1325 FileName const symbols_path = libFileSearch(string(), symbols_name);
1326 if (symbols_path.empty()) {
1327 showFileError(symbols_name);
1331 FileName const enc_path = libFileSearch(string(), enc_name);
1332 if (enc_path.empty()) {
1333 showFileError(enc_name);
1336 encodings.read(enc_path, symbols_path);
1345 /// return the the number of arguments consumed
1346 typedef boost::function<int(string const &, string const &)> cmd_helper;
1348 int parse_dbg(string const & arg, string const &)
1351 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1352 Debug::showTags(lyxerr);
1355 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1357 lyxerr.level(Debug::value(arg));
1358 Debug::showLevel(lyxerr, lyxerr.level());
1363 int parse_help(string const &, string const &)
1366 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1367 "Command line switches (case sensitive):\n"
1368 "\t-help summarize LyX usage\n"
1369 "\t-userdir dir set user directory to dir\n"
1370 "\t-sysdir dir set system directory to dir\n"
1371 "\t-geometry WxH+X+Y set geometry of the main window\n"
1372 "\t-dbg feature[,feature]...\n"
1373 " select the features to debug.\n"
1374 " Type `lyx -dbg' to see the list of features\n"
1375 "\t-x [--execute] command\n"
1376 " where command is a lyx command.\n"
1377 "\t-e [--export] fmt\n"
1378 " where fmt is the export format of choice.\n"
1379 "\t-i [--import] fmt file.xxx\n"
1380 " where fmt is the import format of choice\n"
1381 " and file.xxx is the file to be imported.\n"
1382 "\t-version summarize version and build info\n"
1383 "Check the LyX man page for more details.")) << endl;
1388 int parse_version(string const &, string const &)
1390 lyxerr << "LyX " << lyx_version
1391 << " (" << lyx_release_date << ")" << endl;
1392 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1394 lyxerr << lyx_version_info << endl;
1399 int parse_sysdir(string const & arg, string const &)
1402 Alert::error(_("No system directory"),
1403 _("Missing directory for -sysdir switch"));
1406 cl_system_support = arg;
1410 int parse_userdir(string const & arg, string const &)
1413 Alert::error(_("No user directory"),
1414 _("Missing directory for -userdir switch"));
1417 cl_user_support = arg;
1421 int parse_execute(string const & arg, string const &)
1424 Alert::error(_("Incomplete command"),
1425 _("Missing command string after --execute switch"));
1432 int parse_export(string const & type, string const &)
1435 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1436 "--export switch")) << endl;
1439 batch = "buffer-export " + type;
1444 int parse_import(string const & type, string const & file)
1447 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1448 "--import switch")) << endl;
1452 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1456 batch = "buffer-import " + type + ' ' + file;
1460 int parse_geometry(string const & arg1, string const &)
1463 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1464 // remove also the arg
1467 // don't remove "-geometry"
1476 void LyX::easyParse(int & argc, char * argv[])
1478 std::map<string, cmd_helper> cmdmap;
1480 cmdmap["-dbg"] = parse_dbg;
1481 cmdmap["-help"] = parse_help;
1482 cmdmap["--help"] = parse_help;
1483 cmdmap["-version"] = parse_version;
1484 cmdmap["--version"] = parse_version;
1485 cmdmap["-sysdir"] = parse_sysdir;
1486 cmdmap["-userdir"] = parse_userdir;
1487 cmdmap["-x"] = parse_execute;
1488 cmdmap["--execute"] = parse_execute;
1489 cmdmap["-e"] = parse_export;
1490 cmdmap["--export"] = parse_export;
1491 cmdmap["-i"] = parse_import;
1492 cmdmap["--import"] = parse_import;
1493 cmdmap["-geometry"] = parse_geometry;
1495 for (int i = 1; i < argc; ++i) {
1496 std::map<string, cmd_helper>::const_iterator it
1497 = cmdmap.find(argv[i]);
1499 // don't complain if not found - may be parsed later
1500 if (it == cmdmap.end())
1503 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1504 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1506 int const remove = 1 + it->second(arg, arg2);
1508 // Now, remove used arguments by shifting
1509 // the following ones remove places down.
1512 for (int j = i; j < argc; ++j)
1513 argv[j] = argv[j + remove];
1518 batch_command = batch;
1522 FuncStatus getStatus(FuncRequest const & action)
1524 return LyX::ref().lyxFunc().getStatus(action);
1528 void dispatch(FuncRequest const & action)
1530 LyX::ref().lyxFunc().dispatch(action);
1534 BufferList & theBufferList()
1536 return LyX::ref().bufferList();
1540 LyXFunc & theLyXFunc()
1542 return LyX::ref().lyxFunc();
1546 Server & theServer()
1548 // FIXME: this should not be use_gui dependent
1549 BOOST_ASSERT(use_gui);
1550 return LyX::ref().server();
1554 ServerSocket & theServerSocket()
1556 // FIXME: this should not be use_gui dependent
1557 BOOST_ASSERT(use_gui);
1558 return LyX::ref().socket();
1562 KeyMap & theTopLevelKeymap()
1564 BOOST_ASSERT(use_gui);
1565 return LyX::ref().topLevelKeymap();
1569 Converters & theConverters()
1571 return LyX::ref().converters();
1575 Converters & theSystemConverters()
1577 return LyX::ref().systemConverters();
1581 Movers & theMovers()
1583 return LyX::ref().pimpl_->movers_;
1587 Mover const & getMover(std::string const & fmt)
1589 return LyX::ref().pimpl_->movers_(fmt);
1593 void setMover(std::string const & fmt, std::string const & command)
1595 LyX::ref().pimpl_->movers_.set(fmt, command);
1599 Movers & theSystemMovers()
1601 return LyX::ref().pimpl_->system_movers_;
1605 Messages & getMessages(std::string const & language)
1607 return LyX::ref().getMessages(language);
1611 Messages & getGuiMessages()
1613 return LyX::ref().getGuiMessages();