3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
20 #include "ConverterCache.h"
22 #include "buffer_funcs.h"
23 #include "BufferList.h"
24 #include "Converter.h"
25 #include "CutAndPaste.h"
28 #include "ErrorList.h"
35 #include "LyXAction.h"
39 #include "ModuleList.h"
41 #include "ServerSocket.h"
42 #include "TextClassList.h"
43 #include "MenuBackend.h"
46 #include "ToolbarBackend.h"
48 #include "frontends/alert.h"
49 #include "frontends/Application.h"
50 #include "frontends/Dialogs.h"
51 #include "frontends/Gui.h"
52 #include "frontends/LyXView.h"
54 #include "support/environment.h"
55 #include "support/filetools.h"
56 #include "support/lyxlib.h"
57 #include "support/convert.h"
58 #include "support/ExceptionMessage.h"
59 #include "support/os.h"
60 #include "support/Package.h"
61 #include "support/Path.h"
62 #include "support/Systemcall.h"
64 #include <boost/bind.hpp>
65 #include <boost/filesystem/operations.hpp>
81 #ifndef CXX_GLOBAL_CSTD
87 namespace fs = boost::filesystem;
91 using support::addName;
92 using support::addPath;
93 using support::bformat;
94 using support::changeExtension;
95 using support::createDirectory;
96 using support::createLyXTmpDir;
97 using support::destroyDir;
98 using support::FileName;
99 using support::fileSearch;
100 using support::getEnv;
101 using support::i18nLibFileSearch;
102 using support::libFileSearch;
103 using support::package;
104 using support::prependEnvPath;
105 using support::rtrim;
106 using support::Systemcall;
107 using frontend::LyXView;
109 namespace Alert = frontend::Alert;
110 namespace os = support::os;
114 /// are we using the GUI at all?
116 * We default to true and this is changed to false when the export feature is used.
120 bool quitting; // flag, that we are quitting the program
124 // Filled with the command line arguments "foo" of "-sysdir foo" or
126 string cl_system_support;
127 string cl_user_support;
129 std::string geometryArg;
131 LyX * singleton_ = 0;
133 void showFileError(string const & error)
135 Alert::warning(_("Could not read configuration file"),
136 bformat(_("Error while reading the configuration file\n%1$s.\n"
137 "Please check your installation."), from_utf8(error)));
141 void reconfigureUserLyXDir()
143 string const configure_command = package().configure_command();
145 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
146 support::Path p(package().user_support());
148 one.startscript(Systemcall::Wait, configure_command);
149 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
155 /// The main application class private implementation.
156 struct LyX::Singletons
160 // Set the default User Interface language as soon as possible.
161 // The language used will be derived from the environment
163 messages_["GUI"] = Messages();
165 /// our function handler
168 BufferList buffer_list_;
170 boost::scoped_ptr<KeyMap> toplevel_keymap_;
172 boost::scoped_ptr<Server> lyx_server_;
174 boost::scoped_ptr<ServerSocket> lyx_socket_;
176 boost::scoped_ptr<frontend::Application> application_;
177 /// lyx session, containing lastfiles, lastfilepos, and lastopened
178 boost::scoped_ptr<Session> session_;
180 /// Files to load at start.
181 vector<FileName> files_to_load_;
183 /// The messages translators.
184 map<string, Messages> messages_;
186 /// The file converters.
187 Converters converters_;
189 // The system converters copy after reading lyxrc.defaults.
190 Converters system_converters_;
196 Movers system_movers_;
200 frontend::Application * theApp()
203 return singleton_->pimpl_->application_.get();
216 BOOST_ASSERT(singleton_);
221 LyX const & LyX::cref()
223 BOOST_ASSERT(singleton_);
232 pimpl_.reset(new Singletons);
236 BufferList & LyX::bufferList()
238 return pimpl_->buffer_list_;
242 BufferList const & LyX::bufferList() const
244 return pimpl_->buffer_list_;
248 Session & LyX::session()
250 BOOST_ASSERT(pimpl_->session_.get());
251 return *pimpl_->session_.get();
255 Session const & LyX::session() const
257 BOOST_ASSERT(pimpl_->session_.get());
258 return *pimpl_->session_.get();
262 LyXFunc & LyX::lyxFunc()
264 return pimpl_->lyxfunc_;
268 LyXFunc const & LyX::lyxFunc() const
270 return pimpl_->lyxfunc_;
274 Server & LyX::server()
276 BOOST_ASSERT(pimpl_->lyx_server_.get());
277 return *pimpl_->lyx_server_.get();
281 Server const & LyX::server() const
283 BOOST_ASSERT(pimpl_->lyx_server_.get());
284 return *pimpl_->lyx_server_.get();
288 ServerSocket & LyX::socket()
290 BOOST_ASSERT(pimpl_->lyx_socket_.get());
291 return *pimpl_->lyx_socket_.get();
295 ServerSocket const & LyX::socket() const
297 BOOST_ASSERT(pimpl_->lyx_socket_.get());
298 return *pimpl_->lyx_socket_.get();
302 frontend::Application & LyX::application()
304 BOOST_ASSERT(pimpl_->application_.get());
305 return *pimpl_->application_.get();
309 frontend::Application const & LyX::application() const
311 BOOST_ASSERT(pimpl_->application_.get());
312 return *pimpl_->application_.get();
316 KeyMap & LyX::topLevelKeymap()
318 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
319 return *pimpl_->toplevel_keymap_.get();
323 Converters & LyX::converters()
325 return pimpl_->converters_;
329 Converters & LyX::systemConverters()
331 return pimpl_->system_converters_;
335 KeyMap const & LyX::topLevelKeymap() const
337 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
338 return *pimpl_->toplevel_keymap_.get();
342 Messages & LyX::getMessages(std::string const & language)
344 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
346 if (it != pimpl_->messages_.end())
349 std::pair<map<string, Messages>::iterator, bool> result =
350 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
352 BOOST_ASSERT(result.second);
353 return result.first->second;
357 Messages & LyX::getGuiMessages()
359 return pimpl_->messages_["GUI"];
363 void LyX::setGuiLanguage(std::string const & language)
365 pimpl_->messages_["GUI"] = Messages(language);
369 Buffer const * LyX::updateInset(Inset const * inset) const
371 if (quitting || !inset)
374 Buffer const * buffer_ptr = 0;
375 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
376 vector<int>::const_iterator it = view_ids.begin();
377 vector<int>::const_iterator const end = view_ids.end();
378 for (; it != end; ++it) {
380 pimpl_->application_->gui().view(*it).updateInset(inset);
388 void LyX::hideDialogs(std::string const & name, Inset * inset) const
390 if (quitting || !use_gui)
393 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
394 vector<int>::const_iterator it = view_ids.begin();
395 vector<int>::const_iterator const end = view_ids.end();
396 for (; it != end; ++it)
397 pimpl_->application_->gui().view(*it).getDialogs().
402 int LyX::exec(int & argc, char * argv[])
404 // Here we need to parse the command line. At least
405 // we need to parse for "-dbg" and "-help"
406 easyParse(argc, argv);
409 support::init_package(to_utf8(from_local8bit(argv[0])),
410 cl_system_support, cl_user_support,
411 support::top_build_dir_is_one_level_up);
412 } catch (support::ExceptionMessage const & message) {
413 if (message.type_ == support::ErrorException) {
414 Alert::error(message.title_, message.details_);
416 } else if (message.type_ == support::WarningException) {
417 Alert::warning(message.title_, message.details_);
421 // Reinit the messages machinery in case package() knows
422 // something interesting about the locale directory.
426 // FIXME: create a ConsoleApplication
427 int exit_status = init(argc, argv);
435 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
440 BufferList::iterator begin = pimpl_->buffer_list_.begin();
442 bool final_success = false;
443 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
445 if (buf != buf->getMasterBuffer())
447 bool success = false;
448 buf->dispatch(batch_command, &success);
449 final_success |= success;
452 return !final_success;
455 // Let the frontend parse and remove all arguments that it knows
456 pimpl_->application_.reset(createApplication(argc, argv));
460 // Parse and remove all known arguments in the LyX singleton
461 // Give an error for all remaining ones.
462 int exit_status = init(argc, argv);
464 // Kill the application object before exiting.
465 pimpl_->application_.reset();
472 /* Create a CoreApplication class that will provide the main event loop
473 * and the socket callback registering. With Qt4, only QtCore
474 * library would be needed.
475 * When this is done, a server_mode could be created and the following two
476 * line would be moved out from here.
478 // Note: socket callback must be registered after init(argc, argv)
479 // such that package().temp_dir() is properly initialized.
480 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
481 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
482 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
484 // Start the real execution loop.
485 exit_status = pimpl_->application_->exec();
493 void LyX::prepareExit()
495 // Clear the clipboard and selection stack:
496 cap::clearCutStack();
497 cap::clearSelection();
499 // Set a flag that we do quitting from the program,
500 // so no refreshes are necessary.
503 // close buffers first
504 pimpl_->buffer_list_.closeAll();
506 // do any other cleanup procedures now
507 if (package().temp_dir() != package().system_temp_dir()) {
508 LYXERR(Debug::INFO) << "Deleting tmp dir "
509 << package().temp_dir().absFilename() << endl;
511 if (!destroyDir(package().temp_dir())) {
512 docstring const msg =
513 bformat(_("Unable to remove the temporary directory %1$s"),
514 from_utf8(package().temp_dir().absFilename()));
515 Alert::warning(_("Unable to remove temporary directory"), msg);
520 if (pimpl_->session_)
521 pimpl_->session_->writeFile();
522 pimpl_->session_.reset();
523 pimpl_->lyx_server_.reset();
524 pimpl_->lyx_socket_.reset();
527 // Kill the application object before exiting. This avoids crashes
528 // when exiting on Linux.
529 if (pimpl_->application_)
530 pimpl_->application_.reset();
534 void LyX::earlyExit(int status)
536 BOOST_ASSERT(pimpl_->application_.get());
537 // LyX::pimpl_::application_ is not initialised at this
538 // point so it's safe to just exit after some cleanup.
544 int LyX::init(int & argc, char * argv[])
546 // check for any spurious extra arguments
547 // other than documents
548 for (int argi = 1; argi < argc ; ++argi) {
549 if (argv[argi][0] == '-') {
551 bformat(_("Wrong command line option `%1$s'. Exiting."),
552 from_utf8(argv[argi]))) << endl;
557 // Initialization of LyX (reads lyxrc and more)
558 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
559 bool success = init();
560 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
564 for (int argi = argc - 1; argi >= 1; --argi) {
565 // get absolute path of file and add ".lyx" to
566 // the filename if necessary
567 pimpl_->files_to_load_.push_back(fileSearch(string(),
568 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
569 "lyx", support::allow_unreadable));
573 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
579 void LyX::addFileToLoad(FileName const & fname)
581 vector<FileName>::const_iterator cit = std::find(
582 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
585 if (cit == pimpl_->files_to_load_.end())
586 pimpl_->files_to_load_.push_back(fname);
590 void LyX::loadFiles()
592 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
593 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
595 for (; it != end; ++it) {
599 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
600 if (loadLyXFile(buf, *it)) {
601 ErrorList const & el = buf->errorList("Parse");
603 for_each(el.begin(), el.end(),
604 boost::bind(&LyX::printError, this, _1));
607 pimpl_->buffer_list_.release(buf);
612 void LyX::execBatchCommands()
614 // The advantage of doing this here is that the event loop
615 // is already started. So any need for interaction will be
619 // if reconfiguration is needed.
620 if (textclasslist.empty()) {
621 switch (Alert::prompt(
622 _("No textclass is found"),
623 _("LyX cannot continue because no textclass is found. "
624 "You can either reconfigure normally, or reconfigure using "
625 "default textclasses, or quit LyX."),
632 // regular reconfigure
633 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
636 // reconfigure --without-latex-config
637 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
638 " --without-latex-config"));
641 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
645 // Execute batch commands if available
646 if (batch_command.empty())
649 LYXERR(Debug::INIT) << "About to handle -x '"
650 << batch_command << '\'' << endl;
652 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
656 void LyX::restoreGuiSession()
658 LyXView * view = newLyXView();
660 // if there is no valid class list, do not load any file.
661 if (textclasslist.empty())
664 // if some files were specified at command-line we assume that the
665 // user wants to edit *these* files and not to restore the session.
666 if (!pimpl_->files_to_load_.empty()) {
667 for_each(pimpl_->files_to_load_.begin(),
668 pimpl_->files_to_load_.end(),
669 bind(&LyXView::loadLyXFile, view, _1, true));
670 // clear this list to save a few bytes of RAM
671 pimpl_->files_to_load_.clear();
672 pimpl_->session_->lastOpened().clear();
674 } else if (lyxrc.load_session) {
675 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
676 // do not add to the lastfile list since these files are restored from
677 // last session, and should be already there (regular files), or should
678 // not be added at all (help files).
679 for_each(lastopened.begin(), lastopened.end(),
680 bind(&LyXView::loadLyXFile, view, _1, false));
682 // clear this list to save a few bytes of RAM
683 pimpl_->session_->lastOpened().clear();
686 BufferList::iterator I = pimpl_->buffer_list_.begin();
687 BufferList::iterator end = pimpl_->buffer_list_.end();
688 for (; I != end; ++I) {
690 if (buf != buf->getMasterBuffer())
695 // FIXME: Switch to the last loaded Buffer. This must not be the first one
696 // because the Buffer won't be connected in this case. The correct solution
697 // would be to avoid the manual connection of the current Buffer in LyXView.
698 if (!pimpl_->buffer_list_.empty())
699 view->setBuffer(pimpl_->buffer_list_.last());
703 LyXView * LyX::newLyXView()
708 // determine windows size and position, from lyxrc and/or session
710 unsigned int width = 690;
711 unsigned int height = 510;
712 // default icon size, will be overwritten by stored session value
713 unsigned int iconSizeXY = 0;
714 int maximized = LyXView::NotMaximized;
716 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
717 width = lyxrc.geometry_width;
718 height = lyxrc.geometry_height;
720 // if lyxrc returns (0,0), then use session info
722 string val = session().sessionInfo().load("WindowWidth");
724 width = convert<unsigned int>(val);
725 val = session().sessionInfo().load("WindowHeight");
727 height = convert<unsigned int>(val);
728 val = session().sessionInfo().load("WindowMaximized");
730 maximized = convert<int>(val);
731 val = session().sessionInfo().load("IconSizeXY");
733 iconSizeXY = convert<unsigned int>(val);
736 // if user wants to restore window position
739 if (lyxrc.geometry_xysaved) {
740 string val = session().sessionInfo().load("WindowPosX");
742 posx = convert<int>(val);
743 val = session().sessionInfo().load("WindowPosY");
745 posy = convert<int>(val);
748 if (!geometryArg.empty())
754 // create the main window
755 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
763 The SIGHUP signal does not exist on Windows and does not need to be handled.
765 Windows handles SIGFPE and SIGSEGV signals as expected.
767 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
768 cause a new thread to be spawned. This may well result in unexpected
769 behaviour by the single-threaded LyX.
771 SIGTERM signals will come only from another process actually sending
772 that signal using 'raise' in Windows' POSIX compatability layer. It will
773 not come from the general "terminate process" methods that everyone
774 actually uses (and which can't be trapped). Killing an app 'politely' on
775 Windows involves first sending a WM_CLOSE message, something that is
776 caught already by the Qt frontend.
778 For more information see:
780 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
781 ...signals are mostly useless on Windows for a variety of reasons that are
784 'UNIX Application Migration Guide, Chapter 9'
785 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
787 'How To Terminate an Application "Cleanly" in Win32'
788 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
792 static void error_handler(int err_sig)
794 // Throw away any signals other than the first one received.
795 static sig_atomic_t handling_error = false;
798 handling_error = true;
800 // We have received a signal indicating a fatal error, so
801 // try and save the data ASAP.
802 LyX::cref().emergencyCleanup();
804 // These lyxerr calls may or may not work:
806 // Signals are asynchronous, so the main program may be in a very
807 // fragile state when a signal is processed and thus while a signal
808 // handler function executes.
809 // In general, therefore, we should avoid performing any
810 // I/O operations or calling most library and system functions from
813 // This shouldn't matter here, however, as we've already invoked
818 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
822 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
825 lyxerr << "\nlyx: SIGSEGV signal caught\n"
826 "Sorry, you have found a bug in LyX. "
827 "Please read the bug-reporting instructions "
828 "in Help->Introduction and send us a bug report, "
829 "if necessary. Thanks !\nBye." << endl;
837 // Deinstall the signal handlers
839 signal(SIGHUP, SIG_DFL);
841 signal(SIGINT, SIG_DFL);
842 signal(SIGFPE, SIG_DFL);
843 signal(SIGSEGV, SIG_DFL);
844 signal(SIGTERM, SIG_DFL);
847 if (err_sig == SIGSEGV ||
848 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
850 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
859 void LyX::printError(ErrorItem const & ei)
861 docstring tmp = _("LyX: ") + ei.error + char_type(':')
863 std::cerr << to_utf8(tmp) << std::endl;
867 void LyX::initGuiFont()
869 if (lyxrc.roman_font_name.empty())
870 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
872 if (lyxrc.sans_font_name.empty())
873 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
875 if (lyxrc.typewriter_font_name.empty())
876 lyxrc.typewriter_font_name
877 = pimpl_->application_->typewriterFontName();
884 signal(SIGHUP, error_handler);
886 signal(SIGFPE, error_handler);
887 signal(SIGSEGV, error_handler);
888 signal(SIGINT, error_handler);
889 signal(SIGTERM, error_handler);
890 // SIGPIPE can be safely ignored.
892 lyxrc.tempdir_path = package().temp_dir().absFilename();
893 lyxrc.document_path = package().document_dir().absFilename();
895 if (lyxrc.template_path.empty()) {
896 lyxrc.template_path = addPath(package().system_support().absFilename(),
901 // Read configuration files
904 // This one may have been distributed along with LyX.
905 if (!readRcFile("lyxrc.dist"))
908 // Set the language defined by the distributor.
909 //setGuiLanguage(lyxrc.gui_language);
911 // Set the PATH correctly.
912 #if !defined (USE_POSIX_PACKAGING)
913 // Add the directory containing the LyX executable to the path
914 // so that LyX can find things like tex2lyx.
915 if (package().build_support().empty())
916 prependEnvPath("PATH", package().binary_dir().absFilename());
918 if (!lyxrc.path_prefix.empty())
919 prependEnvPath("PATH", lyxrc.path_prefix);
921 // Check that user LyX directory is ok.
922 if (queryUserLyXDir(package().explicit_user_support()))
923 reconfigureUserLyXDir();
925 // no need for a splash when there is no GUI
930 // This one is generated in user_support directory by lib/configure.py.
931 if (!readRcFile("lyxrc.defaults"))
934 // Query the OS to know what formats are viewed natively
935 formats.setAutoOpen();
937 // Read lyxrc.dist again to be able to override viewer auto-detection.
938 readRcFile("lyxrc.dist");
940 system_lyxrc = lyxrc;
941 system_formats = formats;
942 pimpl_->system_converters_ = pimpl_->converters_;
943 pimpl_->system_movers_ = pimpl_->movers_;
944 system_lcolor = lcolor;
946 // This one is edited through the preferences dialog.
947 if (!readRcFile("preferences"))
950 if (!readEncodingsFile("encodings", "unicodesymbols"))
952 if (!readLanguagesFile("languages"))
956 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
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))
978 if (lyxerr.debugging(Debug::LYXRC))
981 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
982 if (!lyxrc.path_prefix.empty())
983 prependEnvPath("PATH", lyxrc.path_prefix);
985 FileName const document_path(lyxrc.document_path);
986 if (fs::exists(document_path.toFilesystemEncoding()) &&
987 fs::is_directory(document_path.toFilesystemEncoding()))
988 package().document_dir() = document_path;
990 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
991 if (package().temp_dir().empty()) {
992 Alert::error(_("Could not create temporary directory"),
993 bformat(_("Could not create a temporary directory in\n"
994 "%1$s. Make sure that this\n"
995 "path exists and is writable and try again."),
996 from_utf8(lyxrc.tempdir_path)));
997 // createLyXTmpDir() tries sufficiently hard to create a
998 // usable temp dir, so the probability to come here is
999 // close to zero. We therefore don't try to overcome this
1000 // problem with e.g. asking the user for a new path and
1001 // trying again but simply exit.
1005 LYXERR(Debug::INIT) << "LyX tmp dir: `"
1006 << package().temp_dir().absFilename()
1009 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
1010 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1012 // This must happen after package initialization and after lyxrc is
1013 // read, therefore it can't be done by a static object.
1014 ConverterCache::init();
1020 void LyX::defaultKeyBindings(KeyMap * kbmap)
1022 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
1023 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
1024 kbmap->bind("Up", FuncRequest(LFUN_UP));
1025 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
1027 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
1028 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
1029 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
1030 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
1032 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1033 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1034 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1035 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1037 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1038 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1040 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1041 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1043 // kbmap->bindings to enable the use of the numeric keypad
1044 // e.g. Num Lock set
1045 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1046 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1047 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1048 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1049 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1050 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1051 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1052 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1053 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1054 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1055 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1056 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1057 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1058 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1059 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1060 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1061 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1062 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1063 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1064 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1065 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1066 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1067 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1068 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1072 void LyX::emergencyCleanup() const
1074 // what to do about tmpfiles is non-obvious. we would
1075 // like to delete any we find, but our lyxdir might
1076 // contain documents etc. which might be helpful on
1079 pimpl_->buffer_list_.emergencyWriteAll();
1081 if (pimpl_->lyx_server_)
1082 pimpl_->lyx_server_->emergencyCleanup();
1083 pimpl_->lyx_server_.reset();
1084 pimpl_->lyx_socket_.reset();
1089 void LyX::deadKeyBindings(KeyMap * kbmap)
1091 // bindKeyings for transparent handling of deadkeys
1092 // The keysyms are gotten from XFree86 X11R6
1093 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1094 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1095 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1096 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1097 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1098 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1099 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1100 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1101 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1102 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1103 // nothing with this name
1104 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1105 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1106 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1107 // nothing with this name either...
1108 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1109 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1110 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1111 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1117 // return true if file does not exist or is older than configure.py.
1118 bool needsUpdate(string const & file)
1120 // We cannot initialize configure_script directly because the package
1121 // is not initialized yet when static objects are constructed.
1122 static string configure_script;
1123 static bool firstrun = true;
1125 configure_script = FileName(addName(
1126 package().system_support().absFilename(),
1127 "configure.py")).toFilesystemEncoding();
1131 string const absfile = FileName(addName(
1132 package().user_support().absFilename(), file)).toFilesystemEncoding();
1133 return (! fs::exists(absfile))
1134 || (fs::last_write_time(configure_script)
1135 > fs::last_write_time(absfile));
1141 bool LyX::queryUserLyXDir(bool explicit_userdir)
1143 // Does user directory exist?
1144 string const user_support =
1145 package().user_support().toFilesystemEncoding();
1146 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1147 first_start = false;
1149 return needsUpdate("lyxrc.defaults")
1150 || needsUpdate("lyxmodules.lst")
1151 || needsUpdate("textclass.lst")
1152 || needsUpdate("packages.lst");
1155 first_start = !explicit_userdir;
1157 // If the user specified explicitly a directory, ask whether
1158 // to create it. If the user says "no", then exit.
1159 if (explicit_userdir &&
1161 _("Missing user LyX directory"),
1162 bformat(_("You have specified a non-existent user "
1163 "LyX directory, %1$s.\n"
1164 "It is needed to keep your own configuration."),
1165 from_utf8(package().user_support().absFilename())),
1167 _("&Create directory"),
1169 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1170 earlyExit(EXIT_FAILURE);
1173 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1174 from_utf8(package().user_support().absFilename())))
1177 if (!createDirectory(package().user_support(), 0755)) {
1178 // Failed, so let's exit.
1179 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1181 earlyExit(EXIT_FAILURE);
1188 bool LyX::readRcFile(string const & name)
1190 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1192 FileName const lyxrc_path = libFileSearch(string(), name);
1193 if (!lyxrc_path.empty()) {
1195 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1197 if (lyxrc.read(lyxrc_path) < 0) {
1198 showFileError(name);
1202 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1208 // Read the ui file `name'
1209 bool LyX::readUIFile(string const & name, bool include)
1219 struct keyword_item uitags[ui_last - 1] = {
1220 { "include", ui_include },
1221 { "menuset", ui_menuset },
1222 { "toolbars", ui_toolbars },
1223 { "toolbarset", ui_toolbarset }
1226 // Ensure that a file is read only once (prevents include loops)
1227 static std::list<string> uifiles;
1228 std::list<string>::const_iterator it = uifiles.begin();
1229 std::list<string>::const_iterator end = uifiles.end();
1230 it = std::find(it, end, name);
1232 LYXERR(Debug::INIT) << "UI file '" << name
1233 << "' has been read already. "
1234 << "Is this an include loop?"
1239 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1244 ui_path = libFileSearch("ui", name, "inc");
1245 if (ui_path.empty())
1246 ui_path = libFileSearch("ui",
1247 changeExtension(name, "inc"));
1250 ui_path = libFileSearch("ui", name, "ui");
1252 if (ui_path.empty()) {
1253 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1254 showFileError(name);
1258 uifiles.push_back(name);
1260 LYXERR(Debug::INIT) << "Found " << name
1261 << " in " << ui_path << endl;
1262 Lexer lex(uitags, ui_last - 1);
1263 lex.setFile(ui_path);
1265 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1269 if (lyxerr.debugging(Debug::PARSER))
1270 lex.printTable(lyxerr);
1272 while (lex.isOK()) {
1273 switch (lex.lex()) {
1276 string const file = lex.getString();
1277 if (!readUIFile(file, true))
1282 menubackend.read(lex);
1286 toolbarbackend.readToolbars(lex);
1290 toolbarbackend.readToolbarSettings(lex);
1294 if (!rtrim(lex.getString()).empty())
1295 lex.printError("LyX::ReadUIFile: "
1296 "Unknown menu tag: `$$Token'");
1304 // Read the languages file `name'
1305 bool LyX::readLanguagesFile(string const & name)
1307 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1309 FileName const lang_path = libFileSearch(string(), name);
1310 if (lang_path.empty()) {
1311 showFileError(name);
1314 languages.read(lang_path);
1319 // Read the encodings file `name'
1320 bool LyX::readEncodingsFile(string const & enc_name,
1321 string const & symbols_name)
1323 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1324 << symbols_name << "..." << endl;
1326 FileName const symbols_path = libFileSearch(string(), symbols_name);
1327 if (symbols_path.empty()) {
1328 showFileError(symbols_name);
1332 FileName const enc_path = libFileSearch(string(), enc_name);
1333 if (enc_path.empty()) {
1334 showFileError(enc_name);
1337 encodings.read(enc_path, symbols_path);
1346 /// return the the number of arguments consumed
1347 typedef boost::function<int(string const &, string const &)> cmd_helper;
1349 int parse_dbg(string const & arg, string const &)
1352 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1353 Debug::showTags(lyxerr);
1356 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1358 lyxerr.level(Debug::value(arg));
1359 Debug::showLevel(lyxerr, lyxerr.level());
1364 int parse_help(string const &, string const &)
1367 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1368 "Command line switches (case sensitive):\n"
1369 "\t-help summarize LyX usage\n"
1370 "\t-userdir dir set user directory to dir\n"
1371 "\t-sysdir dir set system directory to dir\n"
1372 "\t-geometry WxH+X+Y set geometry of the main window\n"
1373 "\t-dbg feature[,feature]...\n"
1374 " select the features to debug.\n"
1375 " Type `lyx -dbg' to see the list of features\n"
1376 "\t-x [--execute] command\n"
1377 " where command is a lyx command.\n"
1378 "\t-e [--export] fmt\n"
1379 " where fmt is the export format of choice.\n"
1380 "\t-i [--import] fmt file.xxx\n"
1381 " where fmt is the import format of choice\n"
1382 " and file.xxx is the file to be imported.\n"
1383 "\t-version summarize version and build info\n"
1384 "Check the LyX man page for more details.")) << endl;
1389 int parse_version(string const &, string const &)
1391 lyxerr << "LyX " << lyx_version
1392 << " (" << lyx_release_date << ")" << endl;
1393 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1395 lyxerr << lyx_version_info << endl;
1400 int parse_sysdir(string const & arg, string const &)
1403 Alert::error(_("No system directory"),
1404 _("Missing directory for -sysdir switch"));
1407 cl_system_support = arg;
1411 int parse_userdir(string const & arg, string const &)
1414 Alert::error(_("No user directory"),
1415 _("Missing directory for -userdir switch"));
1418 cl_user_support = arg;
1422 int parse_execute(string const & arg, string const &)
1425 Alert::error(_("Incomplete command"),
1426 _("Missing command string after --execute switch"));
1433 int parse_export(string const & type, string const &)
1436 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1437 "--export switch")) << endl;
1440 batch = "buffer-export " + type;
1445 int parse_import(string const & type, string const & file)
1448 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1449 "--import switch")) << endl;
1453 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1457 batch = "buffer-import " + type + ' ' + file;
1461 int parse_geometry(string const & arg1, string const &)
1464 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1465 // remove also the arg
1468 // don't remove "-geometry"
1477 void LyX::easyParse(int & argc, char * argv[])
1479 std::map<string, cmd_helper> cmdmap;
1481 cmdmap["-dbg"] = parse_dbg;
1482 cmdmap["-help"] = parse_help;
1483 cmdmap["--help"] = parse_help;
1484 cmdmap["-version"] = parse_version;
1485 cmdmap["--version"] = parse_version;
1486 cmdmap["-sysdir"] = parse_sysdir;
1487 cmdmap["-userdir"] = parse_userdir;
1488 cmdmap["-x"] = parse_execute;
1489 cmdmap["--execute"] = parse_execute;
1490 cmdmap["-e"] = parse_export;
1491 cmdmap["--export"] = parse_export;
1492 cmdmap["-i"] = parse_import;
1493 cmdmap["--import"] = parse_import;
1494 cmdmap["-geometry"] = parse_geometry;
1496 for (int i = 1; i < argc; ++i) {
1497 std::map<string, cmd_helper>::const_iterator it
1498 = cmdmap.find(argv[i]);
1500 // don't complain if not found - may be parsed later
1501 if (it == cmdmap.end())
1504 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1505 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1507 int const remove = 1 + it->second(arg, arg2);
1509 // Now, remove used arguments by shifting
1510 // the following ones remove places down.
1513 for (int j = i; j < argc; ++j)
1514 argv[j] = argv[j + remove];
1519 batch_command = batch;
1523 FuncStatus getStatus(FuncRequest const & action)
1525 return LyX::ref().lyxFunc().getStatus(action);
1529 void dispatch(FuncRequest const & action)
1531 LyX::ref().lyxFunc().dispatch(action);
1535 BufferList & theBufferList()
1537 return LyX::ref().bufferList();
1541 LyXFunc & theLyXFunc()
1543 return LyX::ref().lyxFunc();
1547 Server & theServer()
1549 // FIXME: this should not be use_gui dependent
1550 BOOST_ASSERT(use_gui);
1551 return LyX::ref().server();
1555 ServerSocket & theServerSocket()
1557 // FIXME: this should not be use_gui dependent
1558 BOOST_ASSERT(use_gui);
1559 return LyX::ref().socket();
1563 KeyMap & theTopLevelKeymap()
1565 BOOST_ASSERT(use_gui);
1566 return LyX::ref().topLevelKeymap();
1570 Converters & theConverters()
1572 return LyX::ref().converters();
1576 Converters & theSystemConverters()
1578 return LyX::ref().systemConverters();
1582 Movers & theMovers()
1584 return LyX::ref().pimpl_->movers_;
1588 Mover const & getMover(std::string const & fmt)
1590 return LyX::ref().pimpl_->movers_(fmt);
1594 void setMover(std::string const & fmt, std::string const & command)
1596 LyX::ref().pimpl_->movers_.set(fmt, command);
1600 Movers & theSystemMovers()
1602 return LyX::ref().pimpl_->system_movers_;
1606 Messages & getMessages(std::string const & language)
1608 return LyX::ref().getMessages(language);
1612 Messages & getGuiMessages()
1614 return LyX::ref().getGuiMessages();