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"
27 #include "ErrorList.h"
35 #include "LyXAction.h"
40 #include "ServerSocket.h"
41 #include "TextClassList.h"
42 #include "MenuBackend.h"
45 #include "ToolbarBackend.h"
47 #include "frontends/alert.h"
48 #include "frontends/Application.h"
49 #include "frontends/Dialogs.h"
50 #include "frontends/Gui.h"
51 #include "frontends/LyXView.h"
53 #include "support/environment.h"
54 #include "support/filetools.h"
55 #include "support/lyxlib.h"
56 #include "support/convert.h"
57 #include "support/ExceptionMessage.h"
58 #include "support/os.h"
59 #include "support/Package.h"
60 #include "support/Path.h"
61 #include "support/Systemcall.h"
63 #include <boost/bind.hpp>
64 #include <boost/filesystem/operations.hpp>
80 #ifndef CXX_GLOBAL_CSTD
86 namespace fs = boost::filesystem;
90 using support::addName;
91 using support::addPath;
92 using support::bformat;
93 using support::changeExtension;
94 using support::createDirectory;
95 using support::createLyXTmpDir;
96 using support::destroyDir;
97 using support::FileName;
98 using support::fileSearch;
99 using support::getEnv;
100 using support::i18nLibFileSearch;
101 using support::libFileSearch;
102 using support::package;
103 using support::prependEnvPath;
104 using support::rtrim;
105 using support::Systemcall;
106 using frontend::LyXView;
108 namespace Alert = frontend::Alert;
109 namespace os = support::os;
113 /// are we using the GUI at all?
115 * We default to true and this is changed to false when the export feature is used.
122 // Filled with the command line arguments "foo" of "-sysdir foo" or
124 string cl_system_support;
125 string cl_user_support;
127 std::string geometryArg;
129 LyX * singleton_ = 0;
131 void showFileError(string const & error)
133 Alert::warning(_("Could not read configuration file"),
134 bformat(_("Error while reading the configuration file\n%1$s.\n"
135 "Please check your installation."), from_utf8(error)));
139 void reconfigureUserLyXDir()
141 string const configure_command = package().configure_command();
143 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
144 support::Path p(package().user_support());
146 one.startscript(Systemcall::Wait, configure_command);
147 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
153 /// The main application class private implementation.
154 struct LyX::Singletons
158 // Set the default User Interface language as soon as possible.
159 // The language used will be derived from the environment
161 messages_["GUI"] = Messages();
163 /// our function handler
166 BufferList buffer_list_;
168 boost::scoped_ptr<KeyMap> toplevel_keymap_;
170 boost::scoped_ptr<Server> lyx_server_;
172 boost::scoped_ptr<ServerSocket> lyx_socket_;
174 boost::scoped_ptr<frontend::Application> application_;
175 /// lyx session, containing lastfiles, lastfilepos, and lastopened
176 boost::scoped_ptr<Session> session_;
178 /// Files to load at start.
179 vector<FileName> files_to_load_;
181 /// The messages translators.
182 map<string, Messages> messages_;
184 /// The file converters.
185 Converters converters_;
187 // The system converters copy after reading lyxrc.defaults.
188 Converters system_converters_;
194 Movers system_movers_;
198 frontend::Application * theApp()
201 return singleton_->pimpl_->application_.get();
214 BOOST_ASSERT(singleton_);
219 LyX const & LyX::cref()
221 BOOST_ASSERT(singleton_);
230 pimpl_.reset(new Singletons);
234 BufferList & LyX::bufferList()
236 return pimpl_->buffer_list_;
240 BufferList const & LyX::bufferList() const
242 return pimpl_->buffer_list_;
246 Session & LyX::session()
248 BOOST_ASSERT(pimpl_->session_.get());
249 return *pimpl_->session_.get();
253 Session const & LyX::session() const
255 BOOST_ASSERT(pimpl_->session_.get());
256 return *pimpl_->session_.get();
260 LyXFunc & LyX::lyxFunc()
262 return pimpl_->lyxfunc_;
266 LyXFunc const & LyX::lyxFunc() const
268 return pimpl_->lyxfunc_;
272 Server & LyX::server()
274 BOOST_ASSERT(pimpl_->lyx_server_.get());
275 return *pimpl_->lyx_server_.get();
279 Server const & LyX::server() const
281 BOOST_ASSERT(pimpl_->lyx_server_.get());
282 return *pimpl_->lyx_server_.get();
286 ServerSocket & LyX::socket()
288 BOOST_ASSERT(pimpl_->lyx_socket_.get());
289 return *pimpl_->lyx_socket_.get();
293 ServerSocket const & LyX::socket() const
295 BOOST_ASSERT(pimpl_->lyx_socket_.get());
296 return *pimpl_->lyx_socket_.get();
300 frontend::Application & LyX::application()
302 BOOST_ASSERT(pimpl_->application_.get());
303 return *pimpl_->application_.get();
307 frontend::Application const & LyX::application() const
309 BOOST_ASSERT(pimpl_->application_.get());
310 return *pimpl_->application_.get();
314 KeyMap & LyX::topLevelKeymap()
316 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
317 return *pimpl_->toplevel_keymap_.get();
321 Converters & LyX::converters()
323 return pimpl_->converters_;
327 Converters & LyX::systemConverters()
329 return pimpl_->system_converters_;
333 KeyMap const & LyX::topLevelKeymap() const
335 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
336 return *pimpl_->toplevel_keymap_.get();
340 Messages & LyX::getMessages(std::string const & language)
342 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
344 if (it != pimpl_->messages_.end())
347 std::pair<map<string, Messages>::iterator, bool> result =
348 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
350 BOOST_ASSERT(result.second);
351 return result.first->second;
355 Messages & LyX::getGuiMessages()
357 return pimpl_->messages_["GUI"];
361 void LyX::setGuiLanguage(std::string const & language)
363 pimpl_->messages_["GUI"] = Messages(language);
367 Buffer const * const LyX::updateInset(Inset const * inset) const
369 if (quitting || !inset)
372 Buffer const * buffer_ptr = 0;
373 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
374 vector<int>::const_iterator it = view_ids.begin();
375 vector<int>::const_iterator const end = view_ids.end();
376 for (; it != end; ++it) {
378 pimpl_->application_->gui().view(*it).updateInset(inset);
386 void LyX::hideDialogs(std::string const & name, Inset * inset) const
388 if (quitting || !use_gui)
391 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
392 vector<int>::const_iterator it = view_ids.begin();
393 vector<int>::const_iterator const end = view_ids.end();
394 for (; it != end; ++it)
395 pimpl_->application_->gui().view(*it).getDialogs().
400 int LyX::exec(int & argc, char * argv[])
402 // Here we need to parse the command line. At least
403 // we need to parse for "-dbg" and "-help"
404 easyParse(argc, argv);
406 try { 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 // Force adding of font path _before_ Application is initialized
453 support::os::addFontResources();
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();
489 // Restore original font resources after Application is destroyed.
490 support::os::restoreFontResources();
496 void LyX::prepareExit()
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 // Execute batch commands if available
619 if (batch_command.empty())
622 LYXERR(Debug::INIT) << "About to handle -x '"
623 << batch_command << '\'' << endl;
625 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
629 void LyX::restoreGuiSession()
631 LyXView * view = newLyXView();
633 // if some files were specified at command-line we assume that the
634 // user wants to edit *these* files and not to restore the session.
635 if (!pimpl_->files_to_load_.empty()) {
636 for_each(pimpl_->files_to_load_.begin(),
637 pimpl_->files_to_load_.end(),
638 bind(&LyXView::loadLyXFile, view, _1, true));
639 // clear this list to save a few bytes of RAM
640 pimpl_->files_to_load_.clear();
641 pimpl_->session_->lastOpened().clear();
643 } else if (lyxrc.load_session) {
644 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
645 // do not add to the lastfile list since these files are restored from
646 // last session, and should be already there (regular files), or should
647 // not be added at all (help files).
648 for_each(lastopened.begin(), lastopened.end(),
649 bind(&LyXView::loadLyXFile, view, _1, false));
651 // clear this list to save a few bytes of RAM
652 pimpl_->session_->lastOpened().clear();
655 BufferList::iterator I = pimpl_->buffer_list_.begin();
656 BufferList::iterator end = pimpl_->buffer_list_.end();
657 for (; I != end; ++I) {
659 if (buf != buf->getMasterBuffer())
664 // FIXME: Switch to the last loaded Buffer. This must not be the first one
665 // because the Buffer won't be connected in this case. The correct solution
666 // would be to avoid the manual connection of the current Buffer in LyXView.
667 view->setBuffer(pimpl_->buffer_list_.last());
671 LyXView * LyX::newLyXView()
676 // determine windows size and position, from lyxrc and/or session
678 unsigned int width = 690;
679 unsigned int height = 510;
680 // default icon size, will be overwritten by stored session value
681 unsigned int iconSizeXY = 0;
682 int maximized = LyXView::NotMaximized;
684 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
685 width = lyxrc.geometry_width;
686 height = lyxrc.geometry_height;
688 // if lyxrc returns (0,0), then use session info
690 string val = session().sessionInfo().load("WindowWidth");
692 width = convert<unsigned int>(val);
693 val = session().sessionInfo().load("WindowHeight");
695 height = convert<unsigned int>(val);
696 val = session().sessionInfo().load("WindowMaximized");
698 maximized = convert<int>(val);
699 val = session().sessionInfo().load("IconSizeXY");
701 iconSizeXY = convert<unsigned int>(val);
704 // if user wants to restore window position
707 if (lyxrc.geometry_xysaved) {
708 string val = session().sessionInfo().load("WindowPosX");
710 posx = convert<int>(val);
711 val = session().sessionInfo().load("WindowPosY");
713 posy = convert<int>(val);
716 if (!geometryArg.empty())
722 // create the main window
723 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
731 The SIGHUP signal does not exist on Windows and does not need to be handled.
733 Windows handles SIGFPE and SIGSEGV signals as expected.
735 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
736 cause a new thread to be spawned. This may well result in unexpected
737 behaviour by the single-threaded LyX.
739 SIGTERM signals will come only from another process actually sending
740 that signal using 'raise' in Windows' POSIX compatability layer. It will
741 not come from the general "terminate process" methods that everyone
742 actually uses (and which can't be trapped). Killing an app 'politely' on
743 Windows involves first sending a WM_CLOSE message, something that is
744 caught already by the Qt frontend.
746 For more information see:
748 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
749 ...signals are mostly useless on Windows for a variety of reasons that are
752 'UNIX Application Migration Guide, Chapter 9'
753 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
755 'How To Terminate an Application "Cleanly" in Win32'
756 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
760 static void error_handler(int err_sig)
762 // Throw away any signals other than the first one received.
763 static sig_atomic_t handling_error = false;
766 handling_error = true;
768 // We have received a signal indicating a fatal error, so
769 // try and save the data ASAP.
770 LyX::cref().emergencyCleanup();
772 // These lyxerr calls may or may not work:
774 // Signals are asynchronous, so the main program may be in a very
775 // fragile state when a signal is processed and thus while a signal
776 // handler function executes.
777 // In general, therefore, we should avoid performing any
778 // I/O operations or calling most library and system functions from
781 // This shouldn't matter here, however, as we've already invoked
786 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
790 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
793 lyxerr << "\nlyx: SIGSEGV signal caught\n"
794 "Sorry, you have found a bug in LyX. "
795 "Please read the bug-reporting instructions "
796 "in Help->Introduction and send us a bug report, "
797 "if necessary. Thanks !\nBye." << endl;
805 // Deinstall the signal handlers
807 signal(SIGHUP, SIG_DFL);
809 signal(SIGINT, SIG_DFL);
810 signal(SIGFPE, SIG_DFL);
811 signal(SIGSEGV, SIG_DFL);
812 signal(SIGTERM, SIG_DFL);
815 if (err_sig == SIGSEGV ||
816 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
818 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
827 void LyX::printError(ErrorItem const & ei)
829 docstring tmp = _("LyX: ") + ei.error + char_type(':')
831 std::cerr << to_utf8(tmp) << std::endl;
835 void LyX::initGuiFont()
837 if (lyxrc.roman_font_name.empty())
838 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
840 if (lyxrc.sans_font_name.empty())
841 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
843 if (lyxrc.typewriter_font_name.empty())
844 lyxrc.typewriter_font_name
845 = pimpl_->application_->typewriterFontName();
852 signal(SIGHUP, error_handler);
854 signal(SIGFPE, error_handler);
855 signal(SIGSEGV, error_handler);
856 signal(SIGINT, error_handler);
857 signal(SIGTERM, error_handler);
858 // SIGPIPE can be safely ignored.
860 lyxrc.tempdir_path = package().temp_dir().absFilename();
861 lyxrc.document_path = package().document_dir().absFilename();
863 if (lyxrc.template_path.empty()) {
864 lyxrc.template_path = addPath(package().system_support().absFilename(),
869 // Read configuration files
872 // This one may have been distributed along with LyX.
873 if (!readRcFile("lyxrc.dist"))
876 // Set the language defined by the distributor.
877 //setGuiLanguage(lyxrc.gui_language);
879 // Set the PATH correctly.
880 #if !defined (USE_POSIX_PACKAGING)
881 // Add the directory containing the LyX executable to the path
882 // so that LyX can find things like tex2lyx.
883 if (package().build_support().empty())
884 prependEnvPath("PATH", package().binary_dir().absFilename());
886 if (!lyxrc.path_prefix.empty())
887 prependEnvPath("PATH", lyxrc.path_prefix);
889 // Check that user LyX directory is ok.
890 if (queryUserLyXDir(package().explicit_user_support()))
891 reconfigureUserLyXDir();
893 // no need for a splash when there is no GUI
898 // This one is generated in user_support directory by lib/configure.py.
899 if (!readRcFile("lyxrc.defaults"))
902 // Query the OS to know what formats are viewed natively
903 formats.setAutoOpen();
905 // Read lyxrc.dist again to be able to override viewer auto-detection.
906 readRcFile("lyxrc.dist");
908 system_lyxrc = lyxrc;
909 system_formats = formats;
910 pimpl_->system_converters_ = pimpl_->converters_;
911 pimpl_->system_movers_ = pimpl_->movers_;
912 system_lcolor = lcolor;
914 // This one is edited through the preferences dialog.
915 if (!readRcFile("preferences"))
918 if (!readEncodingsFile("encodings", "unicodesymbols"))
920 if (!readLanguagesFile("languages"))
924 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
929 // Set the language defined by the user.
930 //setGuiLanguage(lyxrc.gui_language);
933 pimpl_->toplevel_keymap_.reset(new KeyMap);
934 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
935 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
937 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
940 if (!readUIFile(lyxrc.ui_file))
944 if (lyxerr.debugging(Debug::LYXRC))
947 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
948 if (!lyxrc.path_prefix.empty())
949 prependEnvPath("PATH", lyxrc.path_prefix);
951 FileName const document_path(lyxrc.document_path);
952 if (fs::exists(document_path.toFilesystemEncoding()) &&
953 fs::is_directory(document_path.toFilesystemEncoding()))
954 package().document_dir() = document_path;
956 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
957 if (package().temp_dir().empty()) {
958 Alert::error(_("Could not create temporary directory"),
959 bformat(_("Could not create a temporary directory in\n"
960 "%1$s. Make sure that this\n"
961 "path exists and is writable and try again."),
962 from_utf8(lyxrc.tempdir_path)));
963 // createLyXTmpDir() tries sufficiently hard to create a
964 // usable temp dir, so the probability to come here is
965 // close to zero. We therefore don't try to overcome this
966 // problem with e.g. asking the user for a new path and
967 // trying again but simply exit.
971 LYXERR(Debug::INIT) << "LyX tmp dir: `"
972 << package().temp_dir().absFilename()
975 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
976 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
978 // This must happen after package initialization and after lyxrc is
979 // read, therefore it can't be done by a static object.
980 ConverterCache::init();
986 void LyX::defaultKeyBindings(KeyMap * kbmap)
988 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
989 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
990 kbmap->bind("Up", FuncRequest(LFUN_UP));
991 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
993 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
994 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
995 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
996 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
998 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
999 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1000 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1001 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1003 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1004 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1006 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1007 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1009 // kbmap->bindings to enable the use of the numeric keypad
1010 // e.g. Num Lock set
1011 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1012 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1013 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1014 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1015 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1016 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1017 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1018 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1019 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1020 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1021 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1022 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1023 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1024 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1025 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1026 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1027 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1028 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1029 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1030 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1031 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1032 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1033 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1034 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1038 void LyX::emergencyCleanup() const
1040 // what to do about tmpfiles is non-obvious. we would
1041 // like to delete any we find, but our lyxdir might
1042 // contain documents etc. which might be helpful on
1045 pimpl_->buffer_list_.emergencyWriteAll();
1047 if (pimpl_->lyx_server_)
1048 pimpl_->lyx_server_->emergencyCleanup();
1049 pimpl_->lyx_server_.reset();
1050 pimpl_->lyx_socket_.reset();
1055 void LyX::deadKeyBindings(KeyMap * kbmap)
1057 // bindKeyings for transparent handling of deadkeys
1058 // The keysyms are gotten from XFree86 X11R6
1059 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1060 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1061 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1062 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1063 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1064 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1065 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1066 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1067 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1068 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1069 // nothing with this name
1070 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1071 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1072 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1073 // nothing with this name either...
1074 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1075 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1076 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1077 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1083 // return true if file does not exist or is older than configure.py.
1084 bool needsUpdate(string const & file)
1086 // We cannot initialize configure_script directly because the package
1087 // is not initialized yet when static objects are constructed.
1088 static string configure_script;
1089 static bool firstrun = true;
1091 configure_script = FileName(addName(
1092 package().system_support().absFilename(),
1093 "configure.py")).toFilesystemEncoding();
1097 string const absfile = FileName(addName(
1098 package().user_support().absFilename(), file)).toFilesystemEncoding();
1099 return (! fs::exists(absfile))
1100 || (fs::last_write_time(configure_script)
1101 > fs::last_write_time(absfile));
1107 bool LyX::queryUserLyXDir(bool explicit_userdir)
1109 // Does user directory exist?
1110 string const user_support =
1111 package().user_support().toFilesystemEncoding();
1112 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1113 first_start = false;
1115 return needsUpdate("lyxrc.defaults")
1116 || needsUpdate("textclass.lst")
1117 || needsUpdate("packages.lst");
1120 first_start = !explicit_userdir;
1122 // If the user specified explicitly a directory, ask whether
1123 // to create it. If the user says "no", then exit.
1124 if (explicit_userdir &&
1126 _("Missing user LyX directory"),
1127 bformat(_("You have specified a non-existent user "
1128 "LyX directory, %1$s.\n"
1129 "It is needed to keep your own configuration."),
1130 from_utf8(package().user_support().absFilename())),
1132 _("&Create directory"),
1134 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1135 earlyExit(EXIT_FAILURE);
1138 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1139 from_utf8(package().user_support().absFilename())))
1142 if (!createDirectory(package().user_support(), 0755)) {
1143 // Failed, so let's exit.
1144 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1146 earlyExit(EXIT_FAILURE);
1153 bool LyX::readRcFile(string const & name)
1155 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1157 FileName const lyxrc_path = libFileSearch(string(), name);
1158 if (!lyxrc_path.empty()) {
1160 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1162 if (lyxrc.read(lyxrc_path) < 0) {
1163 showFileError(name);
1167 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1173 // Read the ui file `name'
1174 bool LyX::readUIFile(string const & name, bool include)
1184 struct keyword_item uitags[ui_last - 1] = {
1185 { "include", ui_include },
1186 { "menuset", ui_menuset },
1187 { "toolbars", ui_toolbars },
1188 { "toolbarset", ui_toolbarset }
1191 // Ensure that a file is read only once (prevents include loops)
1192 static std::list<string> uifiles;
1193 std::list<string>::const_iterator it = uifiles.begin();
1194 std::list<string>::const_iterator end = uifiles.end();
1195 it = std::find(it, end, name);
1197 LYXERR(Debug::INIT) << "UI file '" << name
1198 << "' has been read already. "
1199 << "Is this an include loop?"
1204 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1209 ui_path = libFileSearch("ui", name, "inc");
1210 if (ui_path.empty())
1211 ui_path = libFileSearch("ui",
1212 changeExtension(name, "inc"));
1215 ui_path = libFileSearch("ui", name, "ui");
1217 if (ui_path.empty()) {
1218 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1219 showFileError(name);
1223 uifiles.push_back(name);
1225 LYXERR(Debug::INIT) << "Found " << name
1226 << " in " << ui_path << endl;
1227 Lexer lex(uitags, ui_last - 1);
1228 lex.setFile(ui_path);
1230 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1234 if (lyxerr.debugging(Debug::PARSER))
1235 lex.printTable(lyxerr);
1237 while (lex.isOK()) {
1238 switch (lex.lex()) {
1241 string const file = lex.getString();
1242 if (!readUIFile(file, true))
1247 menubackend.read(lex);
1251 toolbarbackend.readToolbars(lex);
1255 toolbarbackend.readToolbarSettings(lex);
1259 if (!rtrim(lex.getString()).empty())
1260 lex.printError("LyX::ReadUIFile: "
1261 "Unknown menu tag: `$$Token'");
1269 // Read the languages file `name'
1270 bool LyX::readLanguagesFile(string const & name)
1272 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1274 FileName const lang_path = libFileSearch(string(), name);
1275 if (lang_path.empty()) {
1276 showFileError(name);
1279 languages.read(lang_path);
1284 // Read the encodings file `name'
1285 bool LyX::readEncodingsFile(string const & enc_name,
1286 string const & symbols_name)
1288 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1289 << symbols_name << "..." << endl;
1291 FileName const symbols_path = libFileSearch(string(), symbols_name);
1292 if (symbols_path.empty()) {
1293 showFileError(symbols_name);
1297 FileName const enc_path = libFileSearch(string(), enc_name);
1298 if (enc_path.empty()) {
1299 showFileError(enc_name);
1302 encodings.read(enc_path, symbols_path);
1311 /// return the the number of arguments consumed
1312 typedef boost::function<int(string const &, string const &)> cmd_helper;
1314 int parse_dbg(string const & arg, string const &)
1317 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1318 Debug::showTags(lyxerr);
1321 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1323 lyxerr.level(Debug::value(arg));
1324 Debug::showLevel(lyxerr, lyxerr.level());
1329 int parse_help(string const &, string const &)
1332 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1333 "Command line switches (case sensitive):\n"
1334 "\t-help summarize LyX usage\n"
1335 "\t-userdir dir set user directory to dir\n"
1336 "\t-sysdir dir set system directory to dir\n"
1337 "\t-geometry WxH+X+Y set geometry of the main window\n"
1338 "\t-dbg feature[,feature]...\n"
1339 " select the features to debug.\n"
1340 " Type `lyx -dbg' to see the list of features\n"
1341 "\t-x [--execute] command\n"
1342 " where command is a lyx command.\n"
1343 "\t-e [--export] fmt\n"
1344 " where fmt is the export format of choice.\n"
1345 "\t-i [--import] fmt file.xxx\n"
1346 " where fmt is the import format of choice\n"
1347 " and file.xxx is the file to be imported.\n"
1348 "\t-version summarize version and build info\n"
1349 "Check the LyX man page for more details.")) << endl;
1354 int parse_version(string const &, string const &)
1356 lyxerr << "LyX " << lyx_version
1357 << " (" << lyx_release_date << ")" << endl;
1358 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1360 lyxerr << lyx_version_info << endl;
1365 int parse_sysdir(string const & arg, string const &)
1368 Alert::error(_("No system directory"),
1369 _("Missing directory for -sysdir switch"));
1372 cl_system_support = arg;
1376 int parse_userdir(string const & arg, string const &)
1379 Alert::error(_("No user directory"),
1380 _("Missing directory for -userdir switch"));
1383 cl_user_support = arg;
1387 int parse_execute(string const & arg, string const &)
1390 Alert::error(_("Incomplete command"),
1391 _("Missing command string after --execute switch"));
1398 int parse_export(string const & type, string const &)
1401 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1402 "--export switch")) << endl;
1405 batch = "buffer-export " + type;
1410 int parse_import(string const & type, string const & file)
1413 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1414 "--import switch")) << endl;
1418 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1422 batch = "buffer-import " + type + ' ' + file;
1426 int parse_geometry(string const & arg1, string const &)
1429 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1430 // remove also the arg
1433 // don't remove "-geometry"
1442 void LyX::easyParse(int & argc, char * argv[])
1444 std::map<string, cmd_helper> cmdmap;
1446 cmdmap["-dbg"] = parse_dbg;
1447 cmdmap["-help"] = parse_help;
1448 cmdmap["--help"] = parse_help;
1449 cmdmap["-version"] = parse_version;
1450 cmdmap["--version"] = parse_version;
1451 cmdmap["-sysdir"] = parse_sysdir;
1452 cmdmap["-userdir"] = parse_userdir;
1453 cmdmap["-x"] = parse_execute;
1454 cmdmap["--execute"] = parse_execute;
1455 cmdmap["-e"] = parse_export;
1456 cmdmap["--export"] = parse_export;
1457 cmdmap["-i"] = parse_import;
1458 cmdmap["--import"] = parse_import;
1459 cmdmap["-geometry"] = parse_geometry;
1461 for (int i = 1; i < argc; ++i) {
1462 std::map<string, cmd_helper>::const_iterator it
1463 = cmdmap.find(argv[i]);
1465 // don't complain if not found - may be parsed later
1466 if (it == cmdmap.end())
1469 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1470 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1472 int const remove = 1 + it->second(arg, arg2);
1474 // Now, remove used arguments by shifting
1475 // the following ones remove places down.
1478 for (int j = i; j < argc; ++j)
1479 argv[j] = argv[j + remove];
1484 batch_command = batch;
1488 FuncStatus getStatus(FuncRequest const & action)
1490 return LyX::ref().lyxFunc().getStatus(action);
1494 void dispatch(FuncRequest const & action)
1496 LyX::ref().lyxFunc().dispatch(action);
1500 BufferList & theBufferList()
1502 return LyX::ref().bufferList();
1506 LyXFunc & theLyXFunc()
1508 return LyX::ref().lyxFunc();
1512 Server & theServer()
1514 // FIXME: this should not be use_gui dependent
1515 BOOST_ASSERT(use_gui);
1516 return LyX::ref().server();
1520 ServerSocket & theServerSocket()
1522 // FIXME: this should not be use_gui dependent
1523 BOOST_ASSERT(use_gui);
1524 return LyX::ref().socket();
1528 KeyMap & theTopLevelKeymap()
1530 BOOST_ASSERT(use_gui);
1531 return LyX::ref().topLevelKeymap();
1535 Converters & theConverters()
1537 return LyX::ref().converters();
1541 Converters & theSystemConverters()
1543 return LyX::ref().systemConverters();
1547 Movers & theMovers()
1549 return LyX::ref().pimpl_->movers_;
1553 Mover const & getMover(std::string const & fmt)
1555 return LyX::ref().pimpl_->movers_(fmt);
1559 void setMover(std::string const & fmt, std::string const & command)
1561 LyX::ref().pimpl_->movers_.set(fmt, command);
1565 Movers & theSystemMovers()
1567 return LyX::ref().pimpl_->system_movers_;
1571 Messages & getMessages(std::string const & language)
1573 return LyX::ref().getMessages(language);
1577 Messages & getGuiMessages()
1579 return LyX::ref().getGuiMessages();