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 bool maximize = false;
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 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
653 val = session().sessionInfo().load("IconSizeXY");
655 iconSizeXY = convert<unsigned int>(val);
658 // if user wants to restore window position
661 if (lyxrc.geometry_xysaved) {
662 string val = session().sessionInfo().load("WindowPosX");
664 posx = convert<int>(val);
665 val = session().sessionInfo().load("WindowPosY");
667 posy = convert<int>(val);
670 if (!geometryArg.empty())
676 // create the main window
677 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
685 The SIGHUP signal does not exist on Windows and does not need to be handled.
687 Windows handles SIGFPE and SIGSEGV signals as expected.
689 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
690 cause a new thread to be spawned. This may well result in unexpected
691 behaviour by the single-threaded LyX.
693 SIGTERM signals will come only from another process actually sending
694 that signal using 'raise' in Windows' POSIX compatability layer. It will
695 not come from the general "terminate process" methods that everyone
696 actually uses (and which can't be trapped). Killing an app 'politely' on
697 Windows involves first sending a WM_CLOSE message, something that is
698 caught already by the Qt frontend.
700 For more information see:
702 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
703 ...signals are mostly useless on Windows for a variety of reasons that are
706 'UNIX Application Migration Guide, Chapter 9'
707 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
709 'How To Terminate an Application "Cleanly" in Win32'
710 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
714 static void error_handler(int err_sig)
716 // Throw away any signals other than the first one received.
717 static sig_atomic_t handling_error = false;
720 handling_error = true;
722 // We have received a signal indicating a fatal error, so
723 // try and save the data ASAP.
724 LyX::cref().emergencyCleanup();
726 // These lyxerr calls may or may not work:
728 // Signals are asynchronous, so the main program may be in a very
729 // fragile state when a signal is processed and thus while a signal
730 // handler function executes.
731 // In general, therefore, we should avoid performing any
732 // I/O operations or calling most library and system functions from
735 // This shouldn't matter here, however, as we've already invoked
740 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
744 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
747 lyxerr << "\nlyx: SIGSEGV signal caught\n"
748 "Sorry, you have found a bug in LyX. "
749 "Please read the bug-reporting instructions "
750 "in Help->Introduction and send us a bug report, "
751 "if necessary. Thanks !\nBye." << endl;
759 // Deinstall the signal handlers
761 signal(SIGHUP, SIG_DFL);
763 signal(SIGINT, SIG_DFL);
764 signal(SIGFPE, SIG_DFL);
765 signal(SIGSEGV, SIG_DFL);
766 signal(SIGTERM, SIG_DFL);
769 if (err_sig == SIGSEGV ||
770 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
772 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
781 void LyX::printError(ErrorItem const & ei)
783 docstring tmp = _("LyX: ") + ei.error + char_type(':')
785 std::cerr << to_utf8(tmp) << std::endl;
789 void LyX::initGuiFont()
791 if (lyxrc.roman_font_name.empty())
792 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
794 if (lyxrc.sans_font_name.empty())
795 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
797 if (lyxrc.typewriter_font_name.empty())
798 lyxrc.typewriter_font_name
799 = pimpl_->application_->typewriterFontName();
806 signal(SIGHUP, error_handler);
808 signal(SIGFPE, error_handler);
809 signal(SIGSEGV, error_handler);
810 signal(SIGINT, error_handler);
811 signal(SIGTERM, error_handler);
812 // SIGPIPE can be safely ignored.
814 lyxrc.tempdir_path = package().temp_dir();
815 lyxrc.document_path = package().document_dir();
817 if (lyxrc.template_path.empty()) {
818 lyxrc.template_path = addPath(package().system_support(),
823 // Read configuration files
826 // This one may have been distributed along with LyX.
827 if (!readRcFile("lyxrc.dist"))
830 // Set the language defined by the distributor.
831 //setGuiLanguage(lyxrc.gui_language);
833 // Set the PATH correctly.
834 #if !defined (USE_POSIX_PACKAGING)
835 // Add the directory containing the LyX executable to the path
836 // so that LyX can find things like tex2lyx.
837 if (package().build_support().empty())
838 prependEnvPath("PATH", package().binary_dir());
840 if (!lyxrc.path_prefix.empty())
841 prependEnvPath("PATH", lyxrc.path_prefix);
843 // Check that user LyX directory is ok.
844 if (queryUserLyXDir(package().explicit_user_support()))
845 reconfigureUserLyXDir();
847 // no need for a splash when there is no GUI
852 // This one is generated in user_support directory by lib/configure.py.
853 if (!readRcFile("lyxrc.defaults"))
856 // Query the OS to know what formats are viewed natively
857 formats.setAutoOpen();
859 // Read lyxrc.dist again to be able to override viewer auto-detection.
860 readRcFile("lyxrc.dist");
862 system_lyxrc = lyxrc;
863 system_formats = formats;
864 pimpl_->system_converters_ = pimpl_->converters_;
865 pimpl_->system_movers_ = pimpl_->movers_;
866 system_lcolor = lcolor;
868 // This one is edited through the preferences dialog.
869 if (!readRcFile("preferences"))
872 if (!readEncodingsFile("encodings", "unicodesymbols"))
874 if (!readLanguagesFile("languages"))
878 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
883 // Set the language defined by the user.
884 //setGuiLanguage(lyxrc.gui_language);
887 pimpl_->toplevel_keymap_.reset(new kb_keymap);
888 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
889 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
891 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
894 if (!readUIFile(lyxrc.ui_file))
898 if (lyxerr.debugging(Debug::LYXRC))
901 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
902 if (!lyxrc.path_prefix.empty())
903 prependEnvPath("PATH", lyxrc.path_prefix);
905 FileName const document_path(lyxrc.document_path);
906 if (fs::exists(document_path.toFilesystemEncoding()) &&
907 fs::is_directory(document_path.toFilesystemEncoding()))
908 package().document_dir() = lyxrc.document_path;
910 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
911 if (package().temp_dir().empty()) {
912 Alert::error(_("Could not create temporary directory"),
913 bformat(_("Could not create a temporary directory in\n"
914 "%1$s. Make sure that this\n"
915 "path exists and is writable and try again."),
916 from_utf8(lyxrc.tempdir_path)));
917 // createLyXTmpDir() tries sufficiently hard to create a
918 // usable temp dir, so the probability to come here is
919 // close to zero. We therefore don't try to overcome this
920 // problem with e.g. asking the user for a new path and
921 // trying again but simply exit.
925 if (lyxerr.debugging(Debug::INIT)) {
926 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
929 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
930 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
932 // This must happen after package initialization and after lyxrc is
933 // read, therefore it can't be done by a static object.
934 ConverterCache::init();
940 void LyX::defaultKeyBindings(kb_keymap * kbmap)
942 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
943 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
944 kbmap->bind("Up", FuncRequest(LFUN_UP));
945 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
947 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
948 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
949 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
950 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
952 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
953 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
954 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
955 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
957 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
958 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
960 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
961 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
963 // kbmap->bindings to enable the use of the numeric keypad
965 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
966 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
967 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
968 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
969 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
970 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
971 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
972 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
973 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
974 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
975 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
976 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
977 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
978 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
979 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
980 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
981 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
982 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
983 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
984 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
985 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
986 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
987 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
988 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
992 void LyX::emergencyCleanup() const
994 // what to do about tmpfiles is non-obvious. we would
995 // like to delete any we find, but our lyxdir might
996 // contain documents etc. which might be helpful on
999 pimpl_->buffer_list_.emergencyWriteAll();
1001 if (pimpl_->lyx_server_)
1002 pimpl_->lyx_server_->emergencyCleanup();
1003 pimpl_->lyx_server_.reset();
1004 pimpl_->lyx_socket_.reset();
1009 void LyX::deadKeyBindings(kb_keymap * kbmap)
1011 // bindKeyings for transparent handling of deadkeys
1012 // The keysyms are gotten from XFree86 X11R6
1013 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1014 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1015 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1016 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1017 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1018 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1019 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1020 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1021 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1022 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1023 // nothing with this name
1024 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1025 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1026 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1027 // nothing with this name either...
1028 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1029 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1030 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1031 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1037 // return true if file does not exist or is older than configure.py.
1038 bool needsUpdate(string const & file)
1040 // We cannot initialize configure_script directly because the package
1041 // is not initialized yet when static objects are constructed.
1042 static string configure_script;
1043 static bool firstrun = true;
1045 configure_script = FileName(addName(
1046 package().system_support(),
1047 "configure.py")).toFilesystemEncoding();
1051 string const absfile = FileName(addName(
1052 package().user_support(), file)).toFilesystemEncoding();
1053 return (! fs::exists(absfile))
1054 || (fs::last_write_time(configure_script)
1055 > fs::last_write_time(absfile));
1061 bool LyX::queryUserLyXDir(bool explicit_userdir)
1063 // Does user directory exist?
1064 string const user_support =
1065 FileName(package().user_support()).toFilesystemEncoding();
1066 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1067 first_start = false;
1069 return needsUpdate("lyxrc.defaults")
1070 || needsUpdate("textclass.lst")
1071 || needsUpdate("packages.lst");
1074 first_start = !explicit_userdir;
1076 // If the user specified explicitly a directory, ask whether
1077 // to create it. If the user says "no", then exit.
1078 if (explicit_userdir &&
1080 _("Missing user LyX directory"),
1081 bformat(_("You have specified a non-existent user "
1082 "LyX directory, %1$s.\n"
1083 "It is needed to keep your own configuration."),
1084 from_utf8(package().user_support())),
1086 _("&Create directory"),
1088 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1089 earlyExit(EXIT_FAILURE);
1092 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1093 from_utf8(package().user_support())))
1096 if (!createDirectory(package().user_support(), 0755)) {
1097 // Failed, so let's exit.
1098 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1100 earlyExit(EXIT_FAILURE);
1107 bool LyX::readRcFile(string const & name)
1109 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1111 FileName const lyxrc_path = libFileSearch(string(), name);
1112 if (!lyxrc_path.empty()) {
1114 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1116 if (lyxrc.read(lyxrc_path) < 0) {
1117 showFileError(name);
1121 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1127 // Read the ui file `name'
1128 bool LyX::readUIFile(string const & name, bool include)
1138 struct keyword_item uitags[ui_last - 1] = {
1139 { "include", ui_include },
1140 { "menuset", ui_menuset },
1141 { "toolbar", ui_toolbar },
1142 { "toolbars", ui_toolbars }
1145 // Ensure that a file is read only once (prevents include loops)
1146 static std::list<string> uifiles;
1147 std::list<string>::const_iterator it = uifiles.begin();
1148 std::list<string>::const_iterator end = uifiles.end();
1149 it = std::find(it, end, name);
1151 lyxerr[Debug::INIT] << "UI file '" << name
1152 << "' has been read already. "
1153 << "Is this an include loop?"
1158 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1163 ui_path = libFileSearch("ui", name, "inc");
1164 if (ui_path.empty())
1165 ui_path = libFileSearch("ui",
1166 changeExtension(name, "inc"));
1169 ui_path = libFileSearch("ui", name, "ui");
1171 if (ui_path.empty()) {
1172 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1173 showFileError(name);
1177 uifiles.push_back(name);
1179 lyxerr[Debug::INIT] << "Found " << name
1180 << " in " << ui_path << endl;
1181 LyXLex lex(uitags, ui_last - 1);
1182 lex.setFile(ui_path);
1184 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1188 if (lyxerr.debugging(Debug::PARSER))
1189 lex.printTable(lyxerr);
1191 while (lex.isOK()) {
1192 switch (lex.lex()) {
1195 string const file = lex.getString();
1196 if (!readUIFile(file, true))
1201 menubackend.read(lex);
1205 toolbarbackend.read(lex);
1209 toolbarbackend.readToolbars(lex);
1213 if (!rtrim(lex.getString()).empty())
1214 lex.printError("LyX::ReadUIFile: "
1215 "Unknown menu tag: `$$Token'");
1223 // Read the languages file `name'
1224 bool LyX::readLanguagesFile(string const & name)
1226 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1228 FileName const lang_path = libFileSearch(string(), name);
1229 if (lang_path.empty()) {
1230 showFileError(name);
1233 languages.read(lang_path);
1238 // Read the encodings file `name'
1239 bool LyX::readEncodingsFile(string const & enc_name,
1240 string const & symbols_name)
1242 lyxerr[Debug::INIT] << "About to read " << enc_name << " and "
1243 << symbols_name << "..." << endl;
1245 FileName const symbols_path = libFileSearch(string(), symbols_name);
1246 if (symbols_path.empty()) {
1247 showFileError(symbols_name);
1251 FileName const enc_path = libFileSearch(string(), enc_name);
1252 if (enc_path.empty()) {
1253 showFileError(enc_name);
1256 encodings.read(enc_path, symbols_path);
1265 /// return the the number of arguments consumed
1266 typedef boost::function<int(string const &, string const &)> cmd_helper;
1268 int parse_dbg(string const & arg, string const &)
1271 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1272 Debug::showTags(lyxerr);
1275 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1277 lyxerr.level(Debug::value(arg));
1278 Debug::showLevel(lyxerr, lyxerr.level());
1283 int parse_help(string const &, string const &)
1286 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1287 "Command line switches (case sensitive):\n"
1288 "\t-help summarize LyX usage\n"
1289 "\t-userdir dir set user directory to dir\n"
1290 "\t-sysdir dir set system directory to dir\n"
1291 "\t-geometry WxH+X+Y set geometry of the main window\n"
1292 "\t-dbg feature[,feature]...\n"
1293 " select the features to debug.\n"
1294 " Type `lyx -dbg' to see the list of features\n"
1295 "\t-x [--execute] command\n"
1296 " where command is a lyx command.\n"
1297 "\t-e [--export] fmt\n"
1298 " where fmt is the export format of choice.\n"
1299 "\t-i [--import] fmt file.xxx\n"
1300 " where fmt is the import format of choice\n"
1301 " and file.xxx is the file to be imported.\n"
1302 "\t-version summarize version and build info\n"
1303 "Check the LyX man page for more details.")) << endl;
1308 int parse_version(string const &, string const &)
1310 lyxerr << "LyX " << lyx_version
1311 << " (" << lyx_release_date << ")" << endl;
1312 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1314 lyxerr << lyx_version_info << endl;
1319 int parse_sysdir(string const & arg, string const &)
1322 Alert::error(_("No system directory"),
1323 _("Missing directory for -sysdir switch"));
1326 cl_system_support = arg;
1330 int parse_userdir(string const & arg, string const &)
1333 Alert::error(_("No user directory"),
1334 _("Missing directory for -userdir switch"));
1337 cl_user_support = arg;
1341 int parse_execute(string const & arg, string const &)
1344 Alert::error(_("Incomplete command"),
1345 _("Missing command string after --execute switch"));
1352 int parse_export(string const & type, string const &)
1355 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1356 "--export switch")) << endl;
1359 batch = "buffer-export " + type;
1364 int parse_import(string const & type, string const & file)
1367 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1368 "--import switch")) << endl;
1372 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1376 batch = "buffer-import " + type + ' ' + file;
1380 int parse_geometry(string const & arg1, string const &)
1383 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1384 // remove also the arg
1387 // don't remove "-geometry"
1396 void LyX::easyParse(int & argc, char * argv[])
1398 std::map<string, cmd_helper> cmdmap;
1400 cmdmap["-dbg"] = parse_dbg;
1401 cmdmap["-help"] = parse_help;
1402 cmdmap["--help"] = parse_help;
1403 cmdmap["-version"] = parse_version;
1404 cmdmap["--version"] = parse_version;
1405 cmdmap["-sysdir"] = parse_sysdir;
1406 cmdmap["-userdir"] = parse_userdir;
1407 cmdmap["-x"] = parse_execute;
1408 cmdmap["--execute"] = parse_execute;
1409 cmdmap["-e"] = parse_export;
1410 cmdmap["--export"] = parse_export;
1411 cmdmap["-i"] = parse_import;
1412 cmdmap["--import"] = parse_import;
1413 cmdmap["-geometry"] = parse_geometry;
1415 for (int i = 1; i < argc; ++i) {
1416 std::map<string, cmd_helper>::const_iterator it
1417 = cmdmap.find(argv[i]);
1419 // don't complain if not found - may be parsed later
1420 if (it == cmdmap.end())
1423 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1424 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1426 int const remove = 1 + it->second(arg, arg2);
1428 // Now, remove used arguments by shifting
1429 // the following ones remove places down.
1432 for (int j = i; j < argc; ++j)
1433 argv[j] = argv[j + remove];
1438 batch_command = batch;
1442 FuncStatus getStatus(FuncRequest const & action)
1444 return LyX::ref().lyxFunc().getStatus(action);
1448 void dispatch(FuncRequest const & action)
1450 LyX::ref().lyxFunc().dispatch(action);
1454 BufferList & theBufferList()
1456 return LyX::ref().bufferList();
1460 LyXFunc & theLyXFunc()
1462 return LyX::ref().lyxFunc();
1466 LyXServer & theLyXServer()
1468 // FIXME: this should not be use_gui dependent
1469 BOOST_ASSERT(use_gui);
1470 return LyX::ref().server();
1474 LyXServerSocket & theLyXServerSocket()
1476 // FIXME: this should not be use_gui dependent
1477 BOOST_ASSERT(use_gui);
1478 return LyX::ref().socket();
1482 kb_keymap & theTopLevelKeymap()
1484 BOOST_ASSERT(use_gui);
1485 return LyX::ref().topLevelKeymap();
1489 Converters & theConverters()
1491 return LyX::ref().converters();
1495 Converters & theSystemConverters()
1497 return LyX::ref().systemConverters();
1501 Movers & theMovers()
1503 return LyX::ref().pimpl_->movers_;
1507 Mover const & getMover(std::string const & fmt)
1509 return LyX::ref().pimpl_->movers_(fmt);
1513 void setMover(std::string const & fmt, std::string const & command)
1515 LyX::ref().pimpl_->movers_.set(fmt, command);
1519 Movers & theSystemMovers()
1521 return LyX::ref().pimpl_->system_movers_;
1525 Messages & getMessages(std::string const & language)
1527 return LyX::ref().getMessages(language);
1531 Messages & getGuiMessages()
1533 return LyX::ref().getGuiMessages();