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"
39 #include "lyxserver.h"
40 #include "lyxsocket.h"
41 #include "lyxtextclasslist.h"
42 #include "MenuBackend.h"
45 #include "ToolbarBackend.h"
47 #include "frontends/Alert.h"
48 #include "frontends/Application.h"
49 #include "frontends/Gui.h"
50 #include "frontends/LyXView.h"
52 #include "support/environment.h"
53 #include "support/filetools.h"
54 #include "support/lyxlib.h"
55 #include "support/convert.h"
56 #include "support/ExceptionMessage.h"
57 #include "support/os.h"
58 #include "support/package.h"
59 #include "support/path.h"
60 #include "support/systemcall.h"
62 #include <boost/bind.hpp>
63 #include <boost/filesystem/operations.hpp>
74 using support::addName;
75 using support::addPath;
76 using support::bformat;
77 using support::changeExtension;
78 using support::createDirectory;
79 using support::createLyXTmpDir;
80 using support::destroyDir;
81 using support::FileName;
82 using support::fileSearch;
83 using support::getEnv;
84 using support::i18nLibFileSearch;
85 using support::libFileSearch;
86 using support::package;
87 using support::prependEnvPath;
89 using support::Systemcall;
91 namespace Alert = frontend::Alert;
92 namespace os = support::os;
93 namespace fs = boost::filesystem;
102 #ifndef CXX_GLOBAL_CSTD
109 /// are we using the GUI at all?
111 * We default to true and this is changed to false when the export feature is used.
118 // Filled with the command line arguments "foo" of "-sysdir foo" or
120 string cl_system_support;
121 string cl_user_support;
123 std::string geometryArg;
125 LyX * singleton_ = 0;
127 void showFileError(string const & error)
129 Alert::warning(_("Could not read configuration file"),
130 bformat(_("Error while reading the configuration file\n%1$s.\n"
131 "Please check your installation."), from_utf8(error)));
135 void reconfigureUserLyXDir()
137 string const configure_command = package().configure_command();
139 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
140 support::Path p(package().user_support());
142 one.startscript(Systemcall::Wait, configure_command);
143 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
149 /// The main application class private implementation.
150 struct LyX::Singletons
154 // Set the default User Interface language as soon as possible.
155 // The language used will be derived from the environment
157 messages_["GUI"] = Messages();
159 /// our function handler
162 BufferList buffer_list_;
164 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
166 boost::scoped_ptr<LyXServer> lyx_server_;
168 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
170 boost::scoped_ptr<frontend::Application> application_;
171 /// lyx session, containing lastfiles, lastfilepos, and lastopened
172 boost::scoped_ptr<Session> session_;
174 /// Files to load at start.
175 vector<FileName> files_to_load_;
177 /// The messages translators.
178 map<string, Messages> messages_;
180 /// The file converters.
181 Converters converters_;
183 // The system converters copy after reading lyxrc.defaults.
184 Converters system_converters_;
190 Movers system_movers_;
194 frontend::Application * theApp()
197 return singleton_->pimpl_->application_.get();
210 BOOST_ASSERT(singleton_);
215 LyX const & LyX::cref()
217 BOOST_ASSERT(singleton_);
226 pimpl_.reset(new Singletons);
230 BufferList & LyX::bufferList()
232 return pimpl_->buffer_list_;
236 BufferList const & LyX::bufferList() const
238 return pimpl_->buffer_list_;
242 Session & LyX::session()
244 BOOST_ASSERT(pimpl_->session_.get());
245 return *pimpl_->session_.get();
249 Session const & LyX::session() const
251 BOOST_ASSERT(pimpl_->session_.get());
252 return *pimpl_->session_.get();
256 LyXFunc & LyX::lyxFunc()
258 return pimpl_->lyxfunc_;
262 LyXFunc const & LyX::lyxFunc() const
264 return pimpl_->lyxfunc_;
268 LyXServer & LyX::server()
270 BOOST_ASSERT(pimpl_->lyx_server_.get());
271 return *pimpl_->lyx_server_.get();
275 LyXServer const & LyX::server() const
277 BOOST_ASSERT(pimpl_->lyx_server_.get());
278 return *pimpl_->lyx_server_.get();
282 LyXServerSocket & LyX::socket()
284 BOOST_ASSERT(pimpl_->lyx_socket_.get());
285 return *pimpl_->lyx_socket_.get();
289 LyXServerSocket const & LyX::socket() const
291 BOOST_ASSERT(pimpl_->lyx_socket_.get());
292 return *pimpl_->lyx_socket_.get();
296 frontend::Application & LyX::application()
298 BOOST_ASSERT(pimpl_->application_.get());
299 return *pimpl_->application_.get();
303 frontend::Application const & LyX::application() const
305 BOOST_ASSERT(pimpl_->application_.get());
306 return *pimpl_->application_.get();
310 kb_keymap & LyX::topLevelKeymap()
312 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
313 return *pimpl_->toplevel_keymap_.get();
317 Converters & LyX::converters()
319 return pimpl_->converters_;
323 Converters & LyX::systemConverters()
325 return pimpl_->system_converters_;
329 kb_keymap const & LyX::topLevelKeymap() const
331 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
332 return *pimpl_->toplevel_keymap_.get();
336 Messages & LyX::getMessages(std::string const & language)
338 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
340 if (it != pimpl_->messages_.end())
343 std::pair<map<string, Messages>::iterator, bool> result =
344 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
346 BOOST_ASSERT(result.second);
347 return result.first->second;
351 Messages & LyX::getGuiMessages()
353 return pimpl_->messages_["GUI"];
357 void LyX::setGuiLanguage(std::string const & language)
359 pimpl_->messages_["GUI"] = Messages(language);
363 Buffer const * const LyX::updateInset(InsetBase const * inset) const
368 Buffer const * buffer_ptr = 0;
369 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
370 vector<int>::const_iterator it = view_ids.begin();
371 vector<int>::const_iterator const end = view_ids.end();
372 for (; it != end; ++it) {
374 pimpl_->application_->gui().view(*it).updateInset(inset);
382 int LyX::exec(int & argc, char * argv[])
384 // Here we need to parse the command line. At least
385 // we need to parse for "-dbg" and "-help"
386 easyParse(argc, argv);
388 try { support::init_package(to_utf8(from_local8bit(argv[0])),
389 cl_system_support, cl_user_support,
390 support::top_build_dir_is_one_level_up);
391 } catch (support::ExceptionMessage const & message) {
392 if (message.type_ == support::ErrorException) {
393 Alert::error(message.title_, message.details_);
395 } else if (message.type_ == support::WarningException) {
396 Alert::warning(message.title_, message.details_);
401 // FIXME: create a ConsoleApplication
402 int exit_status = init(argc, argv);
410 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
415 BufferList::iterator begin = pimpl_->buffer_list_.begin();
416 BufferList::iterator end = pimpl_->buffer_list_.end();
418 bool final_success = false;
419 for (BufferList::iterator I = begin; I != end; ++I) {
421 bool success = false;
422 buf->dispatch(batch_command, &success);
423 final_success |= success;
426 return !final_success;
429 // Force adding of font path _before_ Application is initialized
430 support::os::addFontResources();
432 // Let the frontend parse and remove all arguments that it knows
433 pimpl_->application_.reset(createApplication(argc, argv));
437 // Parse and remove all known arguments in the LyX singleton
438 // Give an error for all remaining ones.
439 int exit_status = init(argc, argv);
441 // Kill the application object before exiting.
442 pimpl_->application_.reset();
449 /* Create a CoreApplication class that will provide the main event loop
450 * and the socket callback registering. With Qt4, only QtCore
451 * library would be needed.
452 * When this is done, a server_mode could be created and the following two
453 * line would be moved out from here.
455 // Note: socket callback must be registered after init(argc, argv)
456 // such that package().temp_dir() is properly initialized.
457 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
458 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
459 os::internal_path(package().temp_dir() + "/lyxsocket")));
461 // Start the real execution loop.
462 exit_status = pimpl_->application_->exec();
466 // Restore original font resources after Application is destroyed.
467 support::os::restoreFontResources();
473 void LyX::prepareExit()
475 // Set a flag that we do quitting from the program,
476 // so no refreshes are necessary.
479 // close buffers first
480 pimpl_->buffer_list_.closeAll();
482 // do any other cleanup procedures now
483 if (package().temp_dir() != package().system_temp_dir()) {
484 LYXERR(Debug::INFO) << "Deleting tmp dir "
485 << package().temp_dir() << endl;
487 if (!destroyDir(FileName(package().temp_dir()))) {
488 docstring const msg =
489 bformat(_("Unable to remove the temporary directory %1$s"),
490 from_utf8(package().temp_dir()));
491 Alert::warning(_("Unable to remove temporary directory"), msg);
496 if (pimpl_->session_)
497 pimpl_->session_->writeFile();
498 pimpl_->session_.reset();
499 pimpl_->lyx_server_.reset();
500 pimpl_->lyx_socket_.reset();
503 // Kill the application object before exiting. This avoids crashes
504 // when exiting on Linux.
505 if (pimpl_->application_)
506 pimpl_->application_.reset();
510 void LyX::earlyExit(int status)
512 BOOST_ASSERT(pimpl_->application_.get());
513 // LyX::pimpl_::application_ is not initialised at this
514 // point so it's safe to just exit after some cleanup.
520 int LyX::init(int & argc, char * argv[])
522 // check for any spurious extra arguments
523 // other than documents
524 for (int argi = 1; argi < argc ; ++argi) {
525 if (argv[argi][0] == '-') {
527 bformat(_("Wrong command line option `%1$s'. Exiting."),
528 from_utf8(argv[argi]))) << endl;
533 // Initialization of LyX (reads lyxrc and more)
534 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
535 bool success = init();
536 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
540 for (int argi = argc - 1; argi >= 1; --argi) {
541 // get absolute path of file and add ".lyx" to
542 // the filename if necessary
543 pimpl_->files_to_load_.push_back(fileSearch(string(),
544 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
545 "lyx", support::allow_unreadable));
549 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
555 void LyX::loadFiles()
557 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
558 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
560 for (; it != end; ++it) {
564 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
565 if (loadLyXFile(buf, *it)) {
566 ErrorList const & el = buf->errorList("Parse");
568 for_each(el.begin(), el.end(),
569 boost::bind(&LyX::printError, this, _1));
572 pimpl_->buffer_list_.release(buf);
577 void LyX::execBatchCommands()
579 // The advantage of doing this here is that the event loop
580 // is already started. So any need for interaction will be
584 // Execute batch commands if available
585 if (batch_command.empty())
588 LYXERR(Debug::INIT) << "About to handle -x '"
589 << batch_command << '\'' << endl;
591 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
595 void LyX::restoreGuiSession()
597 LyXView * view = newLyXView();
599 // if some files were specified at command-line we assume that the
600 // user wants to edit *these* files and not to restore the session.
601 if (!pimpl_->files_to_load_.empty()) {
602 for_each(pimpl_->files_to_load_.begin(),
603 pimpl_->files_to_load_.end(),
604 bind(&LyXView::loadLyXFile, view, _1, true));
605 // clear this list to save a few bytes of RAM
606 pimpl_->files_to_load_.clear();
607 pimpl_->session_->lastOpened().clear();
611 if (!lyxrc.load_session)
614 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
615 // do not add to the lastfile list since these files are restored from
616 // last session, and should be already there (regular files), or should
617 // not be added at all (help files).
618 for_each(lastopened.begin(), lastopened.end(),
619 bind(&LyXView::loadLyXFile, view, _1, false));
621 // clear this list to save a few bytes of RAM
622 pimpl_->session_->lastOpened().clear();
626 LyXView * LyX::newLyXView()
631 // determine windows size and position, from lyxrc and/or session
633 unsigned int width = 690;
634 unsigned int height = 510;
635 // default icon size, will be overwritten by stored session value
636 unsigned int iconSizeXY = 0;
637 int maximized = LyXView::NotMaximized;
639 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
640 width = lyxrc.geometry_width;
641 height = lyxrc.geometry_height;
643 // if lyxrc returns (0,0), then use session info
645 string val = session().sessionInfo().load("WindowWidth");
647 width = convert<unsigned int>(val);
648 val = session().sessionInfo().load("WindowHeight");
650 height = convert<unsigned int>(val);
651 val = session().sessionInfo().load("WindowMaximized");
653 maximized = convert<int>(val);
654 val = session().sessionInfo().load("IconSizeXY");
656 iconSizeXY = convert<unsigned int>(val);
659 // if user wants to restore window position
662 if (lyxrc.geometry_xysaved) {
663 string val = session().sessionInfo().load("WindowPosX");
665 posx = convert<int>(val);
666 val = session().sessionInfo().load("WindowPosY");
668 posy = convert<int>(val);
671 if (!geometryArg.empty())
677 // create the main window
678 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
686 The SIGHUP signal does not exist on Windows and does not need to be handled.
688 Windows handles SIGFPE and SIGSEGV signals as expected.
690 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
691 cause a new thread to be spawned. This may well result in unexpected
692 behaviour by the single-threaded LyX.
694 SIGTERM signals will come only from another process actually sending
695 that signal using 'raise' in Windows' POSIX compatability layer. It will
696 not come from the general "terminate process" methods that everyone
697 actually uses (and which can't be trapped). Killing an app 'politely' on
698 Windows involves first sending a WM_CLOSE message, something that is
699 caught already by the Qt frontend.
701 For more information see:
703 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
704 ...signals are mostly useless on Windows for a variety of reasons that are
707 'UNIX Application Migration Guide, Chapter 9'
708 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
710 'How To Terminate an Application "Cleanly" in Win32'
711 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
715 static void error_handler(int err_sig)
717 // Throw away any signals other than the first one received.
718 static sig_atomic_t handling_error = false;
721 handling_error = true;
723 // We have received a signal indicating a fatal error, so
724 // try and save the data ASAP.
725 LyX::cref().emergencyCleanup();
727 // These lyxerr calls may or may not work:
729 // Signals are asynchronous, so the main program may be in a very
730 // fragile state when a signal is processed and thus while a signal
731 // handler function executes.
732 // In general, therefore, we should avoid performing any
733 // I/O operations or calling most library and system functions from
736 // This shouldn't matter here, however, as we've already invoked
741 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
745 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
748 lyxerr << "\nlyx: SIGSEGV signal caught\n"
749 "Sorry, you have found a bug in LyX. "
750 "Please read the bug-reporting instructions "
751 "in Help->Introduction and send us a bug report, "
752 "if necessary. Thanks !\nBye." << endl;
760 // Deinstall the signal handlers
762 signal(SIGHUP, SIG_DFL);
764 signal(SIGINT, SIG_DFL);
765 signal(SIGFPE, SIG_DFL);
766 signal(SIGSEGV, SIG_DFL);
767 signal(SIGTERM, SIG_DFL);
770 if (err_sig == SIGSEGV ||
771 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
773 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
782 void LyX::printError(ErrorItem const & ei)
784 docstring tmp = _("LyX: ") + ei.error + char_type(':')
786 std::cerr << to_utf8(tmp) << std::endl;
790 void LyX::initGuiFont()
792 if (lyxrc.roman_font_name.empty())
793 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
795 if (lyxrc.sans_font_name.empty())
796 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
798 if (lyxrc.typewriter_font_name.empty())
799 lyxrc.typewriter_font_name
800 = pimpl_->application_->typewriterFontName();
807 signal(SIGHUP, error_handler);
809 signal(SIGFPE, error_handler);
810 signal(SIGSEGV, error_handler);
811 signal(SIGINT, error_handler);
812 signal(SIGTERM, error_handler);
813 // SIGPIPE can be safely ignored.
815 lyxrc.tempdir_path = package().temp_dir();
816 lyxrc.document_path = package().document_dir();
818 if (lyxrc.template_path.empty()) {
819 lyxrc.template_path = addPath(package().system_support(),
824 // Read configuration files
827 // This one may have been distributed along with LyX.
828 if (!readRcFile("lyxrc.dist"))
831 // Set the language defined by the distributor.
832 //setGuiLanguage(lyxrc.gui_language);
834 // Set the PATH correctly.
835 #if !defined (USE_POSIX_PACKAGING)
836 // Add the directory containing the LyX executable to the path
837 // so that LyX can find things like tex2lyx.
838 if (package().build_support().empty())
839 prependEnvPath("PATH", package().binary_dir());
841 if (!lyxrc.path_prefix.empty())
842 prependEnvPath("PATH", lyxrc.path_prefix);
844 // Check that user LyX directory is ok.
845 if (queryUserLyXDir(package().explicit_user_support()))
846 reconfigureUserLyXDir();
848 // no need for a splash when there is no GUI
853 // This one is generated in user_support directory by lib/configure.py.
854 if (!readRcFile("lyxrc.defaults"))
857 // Query the OS to know what formats are viewed natively
858 formats.setAutoOpen();
860 // Read lyxrc.dist again to be able to override viewer auto-detection.
861 readRcFile("lyxrc.dist");
863 system_lyxrc = lyxrc;
864 system_formats = formats;
865 pimpl_->system_converters_ = pimpl_->converters_;
866 pimpl_->system_movers_ = pimpl_->movers_;
867 system_lcolor = lcolor;
869 // This one is edited through the preferences dialog.
870 if (!readRcFile("preferences"))
873 if (!readEncodingsFile("encodings", "unicodesymbols"))
875 if (!readLanguagesFile("languages"))
879 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
884 // Set the language defined by the user.
885 //setGuiLanguage(lyxrc.gui_language);
888 pimpl_->toplevel_keymap_.reset(new kb_keymap);
889 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
890 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
892 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
895 if (!readUIFile(lyxrc.ui_file))
899 if (lyxerr.debugging(Debug::LYXRC))
902 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
903 if (!lyxrc.path_prefix.empty())
904 prependEnvPath("PATH", lyxrc.path_prefix);
906 FileName const document_path(lyxrc.document_path);
907 if (fs::exists(document_path.toFilesystemEncoding()) &&
908 fs::is_directory(document_path.toFilesystemEncoding()))
909 package().document_dir() = lyxrc.document_path;
911 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
912 if (package().temp_dir().empty()) {
913 Alert::error(_("Could not create temporary directory"),
914 bformat(_("Could not create a temporary directory in\n"
915 "%1$s. Make sure that this\n"
916 "path exists and is writable and try again."),
917 from_utf8(lyxrc.tempdir_path)));
918 // createLyXTmpDir() tries sufficiently hard to create a
919 // usable temp dir, so the probability to come here is
920 // close to zero. We therefore don't try to overcome this
921 // problem with e.g. asking the user for a new path and
922 // trying again but simply exit.
926 LYXERR(Debug::INIT) << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
928 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
929 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
931 // This must happen after package initialization and after lyxrc is
932 // read, therefore it can't be done by a static object.
933 ConverterCache::init();
939 void LyX::defaultKeyBindings(kb_keymap * kbmap)
941 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
942 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
943 kbmap->bind("Up", FuncRequest(LFUN_UP));
944 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
946 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
947 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
948 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
949 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
951 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
952 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
953 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
954 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
956 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
957 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
959 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
960 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
962 // kbmap->bindings to enable the use of the numeric keypad
964 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
965 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
966 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
967 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
968 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
969 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
970 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
971 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
972 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
973 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
974 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
975 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
976 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
977 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
978 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
979 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
980 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
981 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
982 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
983 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
984 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
985 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
986 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
987 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
991 void LyX::emergencyCleanup() const
993 // what to do about tmpfiles is non-obvious. we would
994 // like to delete any we find, but our lyxdir might
995 // contain documents etc. which might be helpful on
998 pimpl_->buffer_list_.emergencyWriteAll();
1000 if (pimpl_->lyx_server_)
1001 pimpl_->lyx_server_->emergencyCleanup();
1002 pimpl_->lyx_server_.reset();
1003 pimpl_->lyx_socket_.reset();
1008 void LyX::deadKeyBindings(kb_keymap * kbmap)
1010 // bindKeyings for transparent handling of deadkeys
1011 // The keysyms are gotten from XFree86 X11R6
1012 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1013 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1014 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1015 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1016 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1017 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1018 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1019 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1020 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1021 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1022 // nothing with this name
1023 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1024 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1025 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1026 // nothing with this name either...
1027 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1028 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1029 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1030 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1036 // return true if file does not exist or is older than configure.py.
1037 bool needsUpdate(string const & file)
1039 // We cannot initialize configure_script directly because the package
1040 // is not initialized yet when static objects are constructed.
1041 static string configure_script;
1042 static bool firstrun = true;
1044 configure_script = FileName(addName(
1045 package().system_support(),
1046 "configure.py")).toFilesystemEncoding();
1050 string const absfile = FileName(addName(
1051 package().user_support(), file)).toFilesystemEncoding();
1052 return (! fs::exists(absfile))
1053 || (fs::last_write_time(configure_script)
1054 > fs::last_write_time(absfile));
1060 bool LyX::queryUserLyXDir(bool explicit_userdir)
1062 // Does user directory exist?
1063 string const user_support =
1064 FileName(package().user_support()).toFilesystemEncoding();
1065 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1066 first_start = false;
1068 return needsUpdate("lyxrc.defaults")
1069 || needsUpdate("textclass.lst")
1070 || needsUpdate("packages.lst");
1073 first_start = !explicit_userdir;
1075 // If the user specified explicitly a directory, ask whether
1076 // to create it. If the user says "no", then exit.
1077 if (explicit_userdir &&
1079 _("Missing user LyX directory"),
1080 bformat(_("You have specified a non-existent user "
1081 "LyX directory, %1$s.\n"
1082 "It is needed to keep your own configuration."),
1083 from_utf8(package().user_support())),
1085 _("&Create directory"),
1087 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1088 earlyExit(EXIT_FAILURE);
1091 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1092 from_utf8(package().user_support())))
1095 if (!createDirectory(package().user_support(), 0755)) {
1096 // Failed, so let's exit.
1097 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1099 earlyExit(EXIT_FAILURE);
1106 bool LyX::readRcFile(string const & name)
1108 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1110 FileName const lyxrc_path = libFileSearch(string(), name);
1111 if (!lyxrc_path.empty()) {
1113 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1115 if (lyxrc.read(lyxrc_path) < 0) {
1116 showFileError(name);
1120 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1126 // Read the ui file `name'
1127 bool LyX::readUIFile(string const & name, bool include)
1137 struct keyword_item uitags[ui_last - 1] = {
1138 { "include", ui_include },
1139 { "menuset", ui_menuset },
1140 { "toolbar", ui_toolbar },
1141 { "toolbars", ui_toolbars }
1144 // Ensure that a file is read only once (prevents include loops)
1145 static std::list<string> uifiles;
1146 std::list<string>::const_iterator it = uifiles.begin();
1147 std::list<string>::const_iterator end = uifiles.end();
1148 it = std::find(it, end, name);
1150 LYXERR(Debug::INIT) << "UI file '" << name
1151 << "' has been read already. "
1152 << "Is this an include loop?"
1157 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1162 ui_path = libFileSearch("ui", name, "inc");
1163 if (ui_path.empty())
1164 ui_path = libFileSearch("ui",
1165 changeExtension(name, "inc"));
1168 ui_path = libFileSearch("ui", name, "ui");
1170 if (ui_path.empty()) {
1171 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1172 showFileError(name);
1176 uifiles.push_back(name);
1178 LYXERR(Debug::INIT) << "Found " << name
1179 << " in " << ui_path << endl;
1180 LyXLex lex(uitags, ui_last - 1);
1181 lex.setFile(ui_path);
1183 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1187 if (lyxerr.debugging(Debug::PARSER))
1188 lex.printTable(lyxerr);
1190 while (lex.isOK()) {
1191 switch (lex.lex()) {
1194 string const file = lex.getString();
1195 if (!readUIFile(file, true))
1200 menubackend.read(lex);
1204 toolbarbackend.read(lex);
1208 toolbarbackend.readToolbars(lex);
1212 if (!rtrim(lex.getString()).empty())
1213 lex.printError("LyX::ReadUIFile: "
1214 "Unknown menu tag: `$$Token'");
1222 // Read the languages file `name'
1223 bool LyX::readLanguagesFile(string const & name)
1225 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1227 FileName const lang_path = libFileSearch(string(), name);
1228 if (lang_path.empty()) {
1229 showFileError(name);
1232 languages.read(lang_path);
1237 // Read the encodings file `name'
1238 bool LyX::readEncodingsFile(string const & enc_name,
1239 string const & symbols_name)
1241 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1242 << symbols_name << "..." << endl;
1244 FileName const symbols_path = libFileSearch(string(), symbols_name);
1245 if (symbols_path.empty()) {
1246 showFileError(symbols_name);
1250 FileName const enc_path = libFileSearch(string(), enc_name);
1251 if (enc_path.empty()) {
1252 showFileError(enc_name);
1255 encodings.read(enc_path, symbols_path);
1264 /// return the the number of arguments consumed
1265 typedef boost::function<int(string const &, string const &)> cmd_helper;
1267 int parse_dbg(string const & arg, string const &)
1270 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1271 Debug::showTags(lyxerr);
1274 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1276 lyxerr.level(Debug::value(arg));
1277 Debug::showLevel(lyxerr, lyxerr.level());
1282 int parse_help(string const &, string const &)
1285 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1286 "Command line switches (case sensitive):\n"
1287 "\t-help summarize LyX usage\n"
1288 "\t-userdir dir set user directory to dir\n"
1289 "\t-sysdir dir set system directory to dir\n"
1290 "\t-geometry WxH+X+Y set geometry of the main window\n"
1291 "\t-dbg feature[,feature]...\n"
1292 " select the features to debug.\n"
1293 " Type `lyx -dbg' to see the list of features\n"
1294 "\t-x [--execute] command\n"
1295 " where command is a lyx command.\n"
1296 "\t-e [--export] fmt\n"
1297 " where fmt is the export format of choice.\n"
1298 "\t-i [--import] fmt file.xxx\n"
1299 " where fmt is the import format of choice\n"
1300 " and file.xxx is the file to be imported.\n"
1301 "\t-version summarize version and build info\n"
1302 "Check the LyX man page for more details.")) << endl;
1307 int parse_version(string const &, string const &)
1309 lyxerr << "LyX " << lyx_version
1310 << " (" << lyx_release_date << ")" << endl;
1311 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1313 lyxerr << lyx_version_info << endl;
1318 int parse_sysdir(string const & arg, string const &)
1321 Alert::error(_("No system directory"),
1322 _("Missing directory for -sysdir switch"));
1325 cl_system_support = arg;
1329 int parse_userdir(string const & arg, string const &)
1332 Alert::error(_("No user directory"),
1333 _("Missing directory for -userdir switch"));
1336 cl_user_support = arg;
1340 int parse_execute(string const & arg, string const &)
1343 Alert::error(_("Incomplete command"),
1344 _("Missing command string after --execute switch"));
1351 int parse_export(string const & type, string const &)
1354 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1355 "--export switch")) << endl;
1358 batch = "buffer-export " + type;
1363 int parse_import(string const & type, string const & file)
1366 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1367 "--import switch")) << endl;
1371 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1375 batch = "buffer-import " + type + ' ' + file;
1379 int parse_geometry(string const & arg1, string const &)
1382 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1383 // remove also the arg
1386 // don't remove "-geometry"
1395 void LyX::easyParse(int & argc, char * argv[])
1397 std::map<string, cmd_helper> cmdmap;
1399 cmdmap["-dbg"] = parse_dbg;
1400 cmdmap["-help"] = parse_help;
1401 cmdmap["--help"] = parse_help;
1402 cmdmap["-version"] = parse_version;
1403 cmdmap["--version"] = parse_version;
1404 cmdmap["-sysdir"] = parse_sysdir;
1405 cmdmap["-userdir"] = parse_userdir;
1406 cmdmap["-x"] = parse_execute;
1407 cmdmap["--execute"] = parse_execute;
1408 cmdmap["-e"] = parse_export;
1409 cmdmap["--export"] = parse_export;
1410 cmdmap["-i"] = parse_import;
1411 cmdmap["--import"] = parse_import;
1412 cmdmap["-geometry"] = parse_geometry;
1414 for (int i = 1; i < argc; ++i) {
1415 std::map<string, cmd_helper>::const_iterator it
1416 = cmdmap.find(argv[i]);
1418 // don't complain if not found - may be parsed later
1419 if (it == cmdmap.end())
1422 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1423 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1425 int const remove = 1 + it->second(arg, arg2);
1427 // Now, remove used arguments by shifting
1428 // the following ones remove places down.
1431 for (int j = i; j < argc; ++j)
1432 argv[j] = argv[j + remove];
1437 batch_command = batch;
1441 FuncStatus getStatus(FuncRequest const & action)
1443 return LyX::ref().lyxFunc().getStatus(action);
1447 void dispatch(FuncRequest const & action)
1449 LyX::ref().lyxFunc().dispatch(action);
1453 BufferList & theBufferList()
1455 return LyX::ref().bufferList();
1459 LyXFunc & theLyXFunc()
1461 return LyX::ref().lyxFunc();
1465 LyXServer & theLyXServer()
1467 // FIXME: this should not be use_gui dependent
1468 BOOST_ASSERT(use_gui);
1469 return LyX::ref().server();
1473 LyXServerSocket & theLyXServerSocket()
1475 // FIXME: this should not be use_gui dependent
1476 BOOST_ASSERT(use_gui);
1477 return LyX::ref().socket();
1481 kb_keymap & theTopLevelKeymap()
1483 BOOST_ASSERT(use_gui);
1484 return LyX::ref().topLevelKeymap();
1488 Converters & theConverters()
1490 return LyX::ref().converters();
1494 Converters & theSystemConverters()
1496 return LyX::ref().systemConverters();
1500 Movers & theMovers()
1502 return LyX::ref().pimpl_->movers_;
1506 Mover const & getMover(std::string const & fmt)
1508 return LyX::ref().pimpl_->movers_(fmt);
1512 void setMover(std::string const & fmt, std::string const & command)
1514 LyX::ref().pimpl_->movers_.set(fmt, command);
1518 Movers & theSystemMovers()
1520 return LyX::ref().pimpl_->system_movers_;
1524 Messages & getMessages(std::string const & language)
1526 return LyX::ref().getMessages(language);
1530 Messages & getGuiMessages()
1532 return LyX::ref().getGuiMessages();