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>
76 using support::addName;
77 using support::addPath;
78 using support::bformat;
79 using support::changeExtension;
80 using support::createDirectory;
81 using support::createLyXTmpDir;
82 using support::destroyDir;
83 using support::FileName;
84 using support::fileSearch;
85 using support::getEnv;
86 using support::i18nLibFileSearch;
87 using support::libFileSearch;
88 using support::package;
89 using support::prependEnvPath;
91 using support::Systemcall;
93 namespace Alert = frontend::Alert;
94 namespace os = support::os;
95 namespace fs = boost::filesystem;
100 using std::make_pair;
104 #ifndef CXX_GLOBAL_CSTD
111 /// are we using the GUI at all?
113 * We default to true and this is changed to false when the export feature is used.
120 // Filled with the command line arguments "foo" of "-sysdir foo" or
122 string cl_system_support;
123 string cl_user_support;
125 std::string geometryArg;
127 LyX * singleton_ = 0;
129 void showFileError(string const & error)
131 Alert::warning(_("Could not read configuration file"),
132 bformat(_("Error while reading the configuration file\n%1$s.\n"
133 "Please check your installation."), from_utf8(error)));
137 void reconfigureUserLyXDir()
139 string const configure_command = package().configure_command();
141 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
142 support::Path p(package().user_support());
144 one.startscript(Systemcall::Wait, configure_command);
145 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
151 /// The main application class private implementation.
152 struct LyX::Singletons
156 // Set the default User Interface language as soon as possible.
157 // The language used will be derived from the environment
159 messages_["GUI"] = Messages();
161 /// our function handler
164 BufferList buffer_list_;
166 boost::scoped_ptr<KeyMap> toplevel_keymap_;
168 boost::scoped_ptr<Server> lyx_server_;
170 boost::scoped_ptr<ServerSocket> lyx_socket_;
172 boost::scoped_ptr<frontend::Application> application_;
173 /// lyx session, containing lastfiles, lastfilepos, and lastopened
174 boost::scoped_ptr<Session> session_;
176 /// Files to load at start.
177 vector<FileName> files_to_load_;
179 /// The messages translators.
180 map<string, Messages> messages_;
182 /// The file converters.
183 Converters converters_;
185 // The system converters copy after reading lyxrc.defaults.
186 Converters system_converters_;
192 Movers system_movers_;
196 frontend::Application * theApp()
199 return singleton_->pimpl_->application_.get();
212 BOOST_ASSERT(singleton_);
217 LyX const & LyX::cref()
219 BOOST_ASSERT(singleton_);
228 pimpl_.reset(new Singletons);
232 BufferList & LyX::bufferList()
234 return pimpl_->buffer_list_;
238 BufferList const & LyX::bufferList() const
240 return pimpl_->buffer_list_;
244 Session & LyX::session()
246 BOOST_ASSERT(pimpl_->session_.get());
247 return *pimpl_->session_.get();
251 Session const & LyX::session() const
253 BOOST_ASSERT(pimpl_->session_.get());
254 return *pimpl_->session_.get();
258 LyXFunc & LyX::lyxFunc()
260 return pimpl_->lyxfunc_;
264 LyXFunc const & LyX::lyxFunc() const
266 return pimpl_->lyxfunc_;
270 Server & LyX::server()
272 BOOST_ASSERT(pimpl_->lyx_server_.get());
273 return *pimpl_->lyx_server_.get();
277 Server const & LyX::server() const
279 BOOST_ASSERT(pimpl_->lyx_server_.get());
280 return *pimpl_->lyx_server_.get();
284 ServerSocket & LyX::socket()
286 BOOST_ASSERT(pimpl_->lyx_socket_.get());
287 return *pimpl_->lyx_socket_.get();
291 ServerSocket const & LyX::socket() const
293 BOOST_ASSERT(pimpl_->lyx_socket_.get());
294 return *pimpl_->lyx_socket_.get();
298 frontend::Application & LyX::application()
300 BOOST_ASSERT(pimpl_->application_.get());
301 return *pimpl_->application_.get();
305 frontend::Application const & LyX::application() const
307 BOOST_ASSERT(pimpl_->application_.get());
308 return *pimpl_->application_.get();
312 KeyMap & LyX::topLevelKeymap()
314 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
315 return *pimpl_->toplevel_keymap_.get();
319 Converters & LyX::converters()
321 return pimpl_->converters_;
325 Converters & LyX::systemConverters()
327 return pimpl_->system_converters_;
331 KeyMap const & LyX::topLevelKeymap() const
333 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
334 return *pimpl_->toplevel_keymap_.get();
338 Messages & LyX::getMessages(std::string const & language)
340 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
342 if (it != pimpl_->messages_.end())
345 std::pair<map<string, Messages>::iterator, bool> result =
346 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
348 BOOST_ASSERT(result.second);
349 return result.first->second;
353 Messages & LyX::getGuiMessages()
355 return pimpl_->messages_["GUI"];
359 void LyX::setGuiLanguage(std::string const & language)
361 pimpl_->messages_["GUI"] = Messages(language);
365 Buffer const * const LyX::updateInset(Inset const * inset) const
367 if (quitting || !inset)
370 Buffer const * buffer_ptr = 0;
371 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
372 vector<int>::const_iterator it = view_ids.begin();
373 vector<int>::const_iterator const end = view_ids.end();
374 for (; it != end; ++it) {
376 pimpl_->application_->gui().view(*it).updateInset(inset);
384 void LyX::hideDialogs(std::string const & name, Inset * inset) const
386 if (quitting || !use_gui)
389 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
390 vector<int>::const_iterator it = view_ids.begin();
391 vector<int>::const_iterator const end = view_ids.end();
392 for (; it != end; ++it)
393 pimpl_->application_->gui().view(*it).getDialogs().
398 int LyX::exec(int & argc, char * argv[])
400 // Here we need to parse the command line. At least
401 // we need to parse for "-dbg" and "-help"
402 easyParse(argc, argv);
404 try { support::init_package(to_utf8(from_local8bit(argv[0])),
405 cl_system_support, cl_user_support,
406 support::top_build_dir_is_one_level_up);
407 } catch (support::ExceptionMessage const & message) {
408 if (message.type_ == support::ErrorException) {
409 Alert::error(message.title_, message.details_);
411 } else if (message.type_ == support::WarningException) {
412 Alert::warning(message.title_, message.details_);
417 // FIXME: create a ConsoleApplication
418 int exit_status = init(argc, argv);
426 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
431 BufferList::iterator begin = pimpl_->buffer_list_.begin();
432 BufferList::iterator end = pimpl_->buffer_list_.end();
434 bool final_success = false;
435 for (BufferList::iterator I = begin; I != end; ++I) {
437 bool success = false;
438 buf->dispatch(batch_command, &success);
439 final_success |= success;
442 return !final_success;
445 // Force adding of font path _before_ Application is initialized
446 support::os::addFontResources();
448 // Let the frontend parse and remove all arguments that it knows
449 pimpl_->application_.reset(createApplication(argc, argv));
453 // Parse and remove all known arguments in the LyX singleton
454 // Give an error for all remaining ones.
455 int exit_status = init(argc, argv);
457 // Kill the application object before exiting.
458 pimpl_->application_.reset();
465 /* Create a CoreApplication class that will provide the main event loop
466 * and the socket callback registering. With Qt4, only QtCore
467 * library would be needed.
468 * When this is done, a server_mode could be created and the following two
469 * line would be moved out from here.
471 // Note: socket callback must be registered after init(argc, argv)
472 // such that package().temp_dir() is properly initialized.
473 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
474 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
475 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
477 // Start the real execution loop.
478 exit_status = pimpl_->application_->exec();
482 // Restore original font resources after Application is destroyed.
483 support::os::restoreFontResources();
489 void LyX::prepareExit()
491 // Set a flag that we do quitting from the program,
492 // so no refreshes are necessary.
495 // close buffers first
496 pimpl_->buffer_list_.closeAll();
498 // do any other cleanup procedures now
499 if (package().temp_dir() != package().system_temp_dir()) {
500 LYXERR(Debug::INFO) << "Deleting tmp dir "
501 << package().temp_dir().absFilename() << endl;
503 if (!destroyDir(package().temp_dir())) {
504 docstring const msg =
505 bformat(_("Unable to remove the temporary directory %1$s"),
506 from_utf8(package().temp_dir().absFilename()));
507 Alert::warning(_("Unable to remove temporary directory"), msg);
512 if (pimpl_->session_)
513 pimpl_->session_->writeFile();
514 pimpl_->session_.reset();
515 pimpl_->lyx_server_.reset();
516 pimpl_->lyx_socket_.reset();
519 // Kill the application object before exiting. This avoids crashes
520 // when exiting on Linux.
521 if (pimpl_->application_)
522 pimpl_->application_.reset();
526 void LyX::earlyExit(int status)
528 BOOST_ASSERT(pimpl_->application_.get());
529 // LyX::pimpl_::application_ is not initialised at this
530 // point so it's safe to just exit after some cleanup.
536 int LyX::init(int & argc, char * argv[])
538 // check for any spurious extra arguments
539 // other than documents
540 for (int argi = 1; argi < argc ; ++argi) {
541 if (argv[argi][0] == '-') {
543 bformat(_("Wrong command line option `%1$s'. Exiting."),
544 from_utf8(argv[argi]))) << endl;
549 // Initialization of LyX (reads lyxrc and more)
550 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
551 bool success = init();
552 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
556 for (int argi = argc - 1; argi >= 1; --argi) {
557 // get absolute path of file and add ".lyx" to
558 // the filename if necessary
559 pimpl_->files_to_load_.push_back(fileSearch(string(),
560 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
561 "lyx", support::allow_unreadable));
565 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
571 void LyX::addFileToLoad(FileName const & fname)
573 vector<FileName>::const_iterator cit = std::find(
574 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
577 if (cit == pimpl_->files_to_load_.end())
578 pimpl_->files_to_load_.push_back(fname);
582 void LyX::loadFiles()
584 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
585 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
587 for (; it != end; ++it) {
591 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
592 if (loadLyXFile(buf, *it)) {
593 ErrorList const & el = buf->errorList("Parse");
595 for_each(el.begin(), el.end(),
596 boost::bind(&LyX::printError, this, _1));
599 pimpl_->buffer_list_.release(buf);
604 void LyX::execBatchCommands()
606 // The advantage of doing this here is that the event loop
607 // is already started. So any need for interaction will be
611 // Execute batch commands if available
612 if (batch_command.empty())
615 LYXERR(Debug::INIT) << "About to handle -x '"
616 << batch_command << '\'' << endl;
618 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
622 void LyX::restoreGuiSession()
624 LyXView * view = newLyXView();
626 // if some files were specified at command-line we assume that the
627 // user wants to edit *these* files and not to restore the session.
628 if (!pimpl_->files_to_load_.empty()) {
629 for_each(pimpl_->files_to_load_.begin(),
630 pimpl_->files_to_load_.end(),
631 bind(&LyXView::loadLyXFile, view, _1, true, false, false));
632 // clear this list to save a few bytes of RAM
633 pimpl_->files_to_load_.clear();
634 pimpl_->session_->lastOpened().clear();
638 if (!lyxrc.load_session)
641 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
642 // do not add to the lastfile list since these files are restored from
643 // last session, and should be already there (regular files), or should
644 // not be added at all (help files).
645 for_each(lastopened.begin(), lastopened.end(),
646 bind(&LyXView::loadLyXFile, view, _1, false, false, false));
648 // clear this list to save a few bytes of RAM
649 pimpl_->session_->lastOpened().clear();
653 LyXView * LyX::newLyXView()
658 // determine windows size and position, from lyxrc and/or session
660 unsigned int width = 690;
661 unsigned int height = 510;
662 // default icon size, will be overwritten by stored session value
663 unsigned int iconSizeXY = 0;
664 int maximized = LyXView::NotMaximized;
666 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
667 width = lyxrc.geometry_width;
668 height = lyxrc.geometry_height;
670 // if lyxrc returns (0,0), then use session info
672 string val = session().sessionInfo().load("WindowWidth");
674 width = convert<unsigned int>(val);
675 val = session().sessionInfo().load("WindowHeight");
677 height = convert<unsigned int>(val);
678 val = session().sessionInfo().load("WindowMaximized");
680 maximized = convert<int>(val);
681 val = session().sessionInfo().load("IconSizeXY");
683 iconSizeXY = convert<unsigned int>(val);
686 // if user wants to restore window position
689 if (lyxrc.geometry_xysaved) {
690 string val = session().sessionInfo().load("WindowPosX");
692 posx = convert<int>(val);
693 val = session().sessionInfo().load("WindowPosY");
695 posy = convert<int>(val);
698 if (!geometryArg.empty())
704 // create the main window
705 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
713 The SIGHUP signal does not exist on Windows and does not need to be handled.
715 Windows handles SIGFPE and SIGSEGV signals as expected.
717 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
718 cause a new thread to be spawned. This may well result in unexpected
719 behaviour by the single-threaded LyX.
721 SIGTERM signals will come only from another process actually sending
722 that signal using 'raise' in Windows' POSIX compatability layer. It will
723 not come from the general "terminate process" methods that everyone
724 actually uses (and which can't be trapped). Killing an app 'politely' on
725 Windows involves first sending a WM_CLOSE message, something that is
726 caught already by the Qt frontend.
728 For more information see:
730 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
731 ...signals are mostly useless on Windows for a variety of reasons that are
734 'UNIX Application Migration Guide, Chapter 9'
735 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
737 'How To Terminate an Application "Cleanly" in Win32'
738 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
742 static void error_handler(int err_sig)
744 // Throw away any signals other than the first one received.
745 static sig_atomic_t handling_error = false;
748 handling_error = true;
750 // We have received a signal indicating a fatal error, so
751 // try and save the data ASAP.
752 LyX::cref().emergencyCleanup();
754 // These lyxerr calls may or may not work:
756 // Signals are asynchronous, so the main program may be in a very
757 // fragile state when a signal is processed and thus while a signal
758 // handler function executes.
759 // In general, therefore, we should avoid performing any
760 // I/O operations or calling most library and system functions from
763 // This shouldn't matter here, however, as we've already invoked
768 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
772 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
775 lyxerr << "\nlyx: SIGSEGV signal caught\n"
776 "Sorry, you have found a bug in LyX. "
777 "Please read the bug-reporting instructions "
778 "in Help->Introduction and send us a bug report, "
779 "if necessary. Thanks !\nBye." << endl;
787 // Deinstall the signal handlers
789 signal(SIGHUP, SIG_DFL);
791 signal(SIGINT, SIG_DFL);
792 signal(SIGFPE, SIG_DFL);
793 signal(SIGSEGV, SIG_DFL);
794 signal(SIGTERM, SIG_DFL);
797 if (err_sig == SIGSEGV ||
798 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
800 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
809 void LyX::printError(ErrorItem const & ei)
811 docstring tmp = _("LyX: ") + ei.error + char_type(':')
813 std::cerr << to_utf8(tmp) << std::endl;
817 void LyX::initGuiFont()
819 if (lyxrc.roman_font_name.empty())
820 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
822 if (lyxrc.sans_font_name.empty())
823 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
825 if (lyxrc.typewriter_font_name.empty())
826 lyxrc.typewriter_font_name
827 = pimpl_->application_->typewriterFontName();
834 signal(SIGHUP, error_handler);
836 signal(SIGFPE, error_handler);
837 signal(SIGSEGV, error_handler);
838 signal(SIGINT, error_handler);
839 signal(SIGTERM, error_handler);
840 // SIGPIPE can be safely ignored.
842 lyxrc.tempdir_path = package().temp_dir().absFilename();
843 lyxrc.document_path = package().document_dir().absFilename();
845 if (lyxrc.template_path.empty()) {
846 lyxrc.template_path = addPath(package().system_support().absFilename(),
851 // Read configuration files
854 // This one may have been distributed along with LyX.
855 if (!readRcFile("lyxrc.dist"))
858 // Set the language defined by the distributor.
859 //setGuiLanguage(lyxrc.gui_language);
861 // Set the PATH correctly.
862 #if !defined (USE_POSIX_PACKAGING)
863 // Add the directory containing the LyX executable to the path
864 // so that LyX can find things like tex2lyx.
865 if (package().build_support().empty())
866 prependEnvPath("PATH", package().binary_dir().absFilename());
868 if (!lyxrc.path_prefix.empty())
869 prependEnvPath("PATH", lyxrc.path_prefix);
871 // Check that user LyX directory is ok.
872 if (queryUserLyXDir(package().explicit_user_support()))
873 reconfigureUserLyXDir();
875 // no need for a splash when there is no GUI
880 // This one is generated in user_support directory by lib/configure.py.
881 if (!readRcFile("lyxrc.defaults"))
884 // Query the OS to know what formats are viewed natively
885 formats.setAutoOpen();
887 // Read lyxrc.dist again to be able to override viewer auto-detection.
888 readRcFile("lyxrc.dist");
890 system_lyxrc = lyxrc;
891 system_formats = formats;
892 pimpl_->system_converters_ = pimpl_->converters_;
893 pimpl_->system_movers_ = pimpl_->movers_;
894 system_lcolor = lcolor;
896 // This one is edited through the preferences dialog.
897 if (!readRcFile("preferences"))
900 if (!readEncodingsFile("encodings", "unicodesymbols"))
902 if (!readLanguagesFile("languages"))
906 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
911 // Set the language defined by the user.
912 //setGuiLanguage(lyxrc.gui_language);
915 pimpl_->toplevel_keymap_.reset(new KeyMap);
916 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
917 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
919 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
922 if (!readUIFile(lyxrc.ui_file))
926 if (lyxerr.debugging(Debug::LYXRC))
929 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
930 if (!lyxrc.path_prefix.empty())
931 prependEnvPath("PATH", lyxrc.path_prefix);
933 FileName const document_path(lyxrc.document_path);
934 if (fs::exists(document_path.toFilesystemEncoding()) &&
935 fs::is_directory(document_path.toFilesystemEncoding()))
936 package().document_dir() = document_path;
938 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
939 if (package().temp_dir().empty()) {
940 Alert::error(_("Could not create temporary directory"),
941 bformat(_("Could not create a temporary directory in\n"
942 "%1$s. Make sure that this\n"
943 "path exists and is writable and try again."),
944 from_utf8(lyxrc.tempdir_path)));
945 // createLyXTmpDir() tries sufficiently hard to create a
946 // usable temp dir, so the probability to come here is
947 // close to zero. We therefore don't try to overcome this
948 // problem with e.g. asking the user for a new path and
949 // trying again but simply exit.
953 LYXERR(Debug::INIT) << "LyX tmp dir: `"
954 << package().temp_dir().absFilename()
957 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
958 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
960 // This must happen after package initialization and after lyxrc is
961 // read, therefore it can't be done by a static object.
962 ConverterCache::init();
968 void LyX::defaultKeyBindings(KeyMap * kbmap)
970 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
971 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
972 kbmap->bind("Up", FuncRequest(LFUN_UP));
973 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
975 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
976 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
977 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
978 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
980 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
981 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
982 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
983 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
985 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
986 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
988 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
989 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
991 // kbmap->bindings to enable the use of the numeric keypad
993 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
994 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
995 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
996 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
997 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
998 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
999 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1000 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1001 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1002 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1003 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1004 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1005 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1006 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1007 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1008 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1009 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1010 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1011 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1012 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1013 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1014 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1015 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1016 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1020 void LyX::emergencyCleanup() const
1022 // what to do about tmpfiles is non-obvious. we would
1023 // like to delete any we find, but our lyxdir might
1024 // contain documents etc. which might be helpful on
1027 pimpl_->buffer_list_.emergencyWriteAll();
1029 if (pimpl_->lyx_server_)
1030 pimpl_->lyx_server_->emergencyCleanup();
1031 pimpl_->lyx_server_.reset();
1032 pimpl_->lyx_socket_.reset();
1037 void LyX::deadKeyBindings(KeyMap * kbmap)
1039 // bindKeyings for transparent handling of deadkeys
1040 // The keysyms are gotten from XFree86 X11R6
1041 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1042 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1043 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1044 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1045 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1046 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1047 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1048 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1049 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1050 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1051 // nothing with this name
1052 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1053 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1054 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1055 // nothing with this name either...
1056 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1057 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1058 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1059 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1065 // return true if file does not exist or is older than configure.py.
1066 bool needsUpdate(string const & file)
1068 // We cannot initialize configure_script directly because the package
1069 // is not initialized yet when static objects are constructed.
1070 static string configure_script;
1071 static bool firstrun = true;
1073 configure_script = FileName(addName(
1074 package().system_support().absFilename(),
1075 "configure.py")).toFilesystemEncoding();
1079 string const absfile = FileName(addName(
1080 package().user_support().absFilename(), file)).toFilesystemEncoding();
1081 return (! fs::exists(absfile))
1082 || (fs::last_write_time(configure_script)
1083 > fs::last_write_time(absfile));
1089 bool LyX::queryUserLyXDir(bool explicit_userdir)
1091 // Does user directory exist?
1092 string const user_support =
1093 package().user_support().toFilesystemEncoding();
1094 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1095 first_start = false;
1097 return needsUpdate("lyxrc.defaults")
1098 || needsUpdate("textclass.lst")
1099 || needsUpdate("packages.lst");
1102 first_start = !explicit_userdir;
1104 // If the user specified explicitly a directory, ask whether
1105 // to create it. If the user says "no", then exit.
1106 if (explicit_userdir &&
1108 _("Missing user LyX directory"),
1109 bformat(_("You have specified a non-existent user "
1110 "LyX directory, %1$s.\n"
1111 "It is needed to keep your own configuration."),
1112 from_utf8(package().user_support().absFilename())),
1114 _("&Create directory"),
1116 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1117 earlyExit(EXIT_FAILURE);
1120 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1121 from_utf8(package().user_support().absFilename())))
1124 if (!createDirectory(package().user_support(), 0755)) {
1125 // Failed, so let's exit.
1126 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1128 earlyExit(EXIT_FAILURE);
1135 bool LyX::readRcFile(string const & name)
1137 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1139 FileName const lyxrc_path = libFileSearch(string(), name);
1140 if (!lyxrc_path.empty()) {
1142 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1144 if (lyxrc.read(lyxrc_path) < 0) {
1145 showFileError(name);
1149 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1155 // Read the ui file `name'
1156 bool LyX::readUIFile(string const & name, bool include)
1166 struct keyword_item uitags[ui_last - 1] = {
1167 { "include", ui_include },
1168 { "menuset", ui_menuset },
1169 { "toolbars", ui_toolbars },
1170 { "toolbarset", ui_toolbarset }
1173 // Ensure that a file is read only once (prevents include loops)
1174 static std::list<string> uifiles;
1175 std::list<string>::const_iterator it = uifiles.begin();
1176 std::list<string>::const_iterator end = uifiles.end();
1177 it = std::find(it, end, name);
1179 LYXERR(Debug::INIT) << "UI file '" << name
1180 << "' has been read already. "
1181 << "Is this an include loop?"
1186 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1191 ui_path = libFileSearch("ui", name, "inc");
1192 if (ui_path.empty())
1193 ui_path = libFileSearch("ui",
1194 changeExtension(name, "inc"));
1197 ui_path = libFileSearch("ui", name, "ui");
1199 if (ui_path.empty()) {
1200 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1201 showFileError(name);
1205 uifiles.push_back(name);
1207 LYXERR(Debug::INIT) << "Found " << name
1208 << " in " << ui_path << endl;
1209 Lexer lex(uitags, ui_last - 1);
1210 lex.setFile(ui_path);
1212 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1216 if (lyxerr.debugging(Debug::PARSER))
1217 lex.printTable(lyxerr);
1219 while (lex.isOK()) {
1220 switch (lex.lex()) {
1223 string const file = lex.getString();
1224 if (!readUIFile(file, true))
1229 menubackend.read(lex);
1233 toolbarbackend.readToolbars(lex);
1237 toolbarbackend.readToolbarSettings(lex);
1241 if (!rtrim(lex.getString()).empty())
1242 lex.printError("LyX::ReadUIFile: "
1243 "Unknown menu tag: `$$Token'");
1251 // Read the languages file `name'
1252 bool LyX::readLanguagesFile(string const & name)
1254 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1256 FileName const lang_path = libFileSearch(string(), name);
1257 if (lang_path.empty()) {
1258 showFileError(name);
1261 languages.read(lang_path);
1266 // Read the encodings file `name'
1267 bool LyX::readEncodingsFile(string const & enc_name,
1268 string const & symbols_name)
1270 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1271 << symbols_name << "..." << endl;
1273 FileName const symbols_path = libFileSearch(string(), symbols_name);
1274 if (symbols_path.empty()) {
1275 showFileError(symbols_name);
1279 FileName const enc_path = libFileSearch(string(), enc_name);
1280 if (enc_path.empty()) {
1281 showFileError(enc_name);
1284 encodings.read(enc_path, symbols_path);
1293 /// return the the number of arguments consumed
1294 typedef boost::function<int(string const &, string const &)> cmd_helper;
1296 int parse_dbg(string const & arg, string const &)
1299 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1300 Debug::showTags(lyxerr);
1303 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1305 lyxerr.level(Debug::value(arg));
1306 Debug::showLevel(lyxerr, lyxerr.level());
1311 int parse_help(string const &, string const &)
1314 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1315 "Command line switches (case sensitive):\n"
1316 "\t-help summarize LyX usage\n"
1317 "\t-userdir dir set user directory to dir\n"
1318 "\t-sysdir dir set system directory to dir\n"
1319 "\t-geometry WxH+X+Y set geometry of the main window\n"
1320 "\t-dbg feature[,feature]...\n"
1321 " select the features to debug.\n"
1322 " Type `lyx -dbg' to see the list of features\n"
1323 "\t-x [--execute] command\n"
1324 " where command is a lyx command.\n"
1325 "\t-e [--export] fmt\n"
1326 " where fmt is the export format of choice.\n"
1327 "\t-i [--import] fmt file.xxx\n"
1328 " where fmt is the import format of choice\n"
1329 " and file.xxx is the file to be imported.\n"
1330 "\t-version summarize version and build info\n"
1331 "Check the LyX man page for more details.")) << endl;
1336 int parse_version(string const &, string const &)
1338 lyxerr << "LyX " << lyx_version
1339 << " (" << lyx_release_date << ")" << endl;
1340 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1342 lyxerr << lyx_version_info << endl;
1347 int parse_sysdir(string const & arg, string const &)
1350 Alert::error(_("No system directory"),
1351 _("Missing directory for -sysdir switch"));
1354 cl_system_support = arg;
1358 int parse_userdir(string const & arg, string const &)
1361 Alert::error(_("No user directory"),
1362 _("Missing directory for -userdir switch"));
1365 cl_user_support = arg;
1369 int parse_execute(string const & arg, string const &)
1372 Alert::error(_("Incomplete command"),
1373 _("Missing command string after --execute switch"));
1380 int parse_export(string const & type, string const &)
1383 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1384 "--export switch")) << endl;
1387 batch = "buffer-export " + type;
1392 int parse_import(string const & type, string const & file)
1395 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1396 "--import switch")) << endl;
1400 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1404 batch = "buffer-import " + type + ' ' + file;
1408 int parse_geometry(string const & arg1, string const &)
1411 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1412 // remove also the arg
1415 // don't remove "-geometry"
1424 void LyX::easyParse(int & argc, char * argv[])
1426 std::map<string, cmd_helper> cmdmap;
1428 cmdmap["-dbg"] = parse_dbg;
1429 cmdmap["-help"] = parse_help;
1430 cmdmap["--help"] = parse_help;
1431 cmdmap["-version"] = parse_version;
1432 cmdmap["--version"] = parse_version;
1433 cmdmap["-sysdir"] = parse_sysdir;
1434 cmdmap["-userdir"] = parse_userdir;
1435 cmdmap["-x"] = parse_execute;
1436 cmdmap["--execute"] = parse_execute;
1437 cmdmap["-e"] = parse_export;
1438 cmdmap["--export"] = parse_export;
1439 cmdmap["-i"] = parse_import;
1440 cmdmap["--import"] = parse_import;
1441 cmdmap["-geometry"] = parse_geometry;
1443 for (int i = 1; i < argc; ++i) {
1444 std::map<string, cmd_helper>::const_iterator it
1445 = cmdmap.find(argv[i]);
1447 // don't complain if not found - may be parsed later
1448 if (it == cmdmap.end())
1451 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1452 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1454 int const remove = 1 + it->second(arg, arg2);
1456 // Now, remove used arguments by shifting
1457 // the following ones remove places down.
1460 for (int j = i; j < argc; ++j)
1461 argv[j] = argv[j + remove];
1466 batch_command = batch;
1470 FuncStatus getStatus(FuncRequest const & action)
1472 return LyX::ref().lyxFunc().getStatus(action);
1476 void dispatch(FuncRequest const & action)
1478 LyX::ref().lyxFunc().dispatch(action);
1482 BufferList & theBufferList()
1484 return LyX::ref().bufferList();
1488 LyXFunc & theLyXFunc()
1490 return LyX::ref().lyxFunc();
1494 Server & theServer()
1496 // FIXME: this should not be use_gui dependent
1497 BOOST_ASSERT(use_gui);
1498 return LyX::ref().server();
1502 ServerSocket & theServerSocket()
1504 // FIXME: this should not be use_gui dependent
1505 BOOST_ASSERT(use_gui);
1506 return LyX::ref().socket();
1510 KeyMap & theTopLevelKeymap()
1512 BOOST_ASSERT(use_gui);
1513 return LyX::ref().topLevelKeymap();
1517 Converters & theConverters()
1519 return LyX::ref().converters();
1523 Converters & theSystemConverters()
1525 return LyX::ref().systemConverters();
1529 Movers & theMovers()
1531 return LyX::ref().pimpl_->movers_;
1535 Mover const & getMover(std::string const & fmt)
1537 return LyX::ref().pimpl_->movers_(fmt);
1541 void setMover(std::string const & fmt, std::string const & command)
1543 LyX::ref().pimpl_->movers_.set(fmt, command);
1547 Movers & theSystemMovers()
1549 return LyX::ref().pimpl_->system_movers_;
1553 Messages & getMessages(std::string const & language)
1555 return LyX::ref().getMessages(language);
1559 Messages & getGuiMessages()
1561 return LyX::ref().getGuiMessages();