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 if (lyxerr.debugging(Debug::INIT)) {
927 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
930 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
931 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
933 // This must happen after package initialization and after lyxrc is
934 // read, therefore it can't be done by a static object.
935 ConverterCache::init();
941 void LyX::defaultKeyBindings(kb_keymap * kbmap)
943 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
944 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
945 kbmap->bind("Up", FuncRequest(LFUN_UP));
946 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
948 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
949 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
950 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
951 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
953 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
954 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
955 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
956 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
958 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
959 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
961 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
962 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
964 // kbmap->bindings to enable the use of the numeric keypad
966 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
967 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
968 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
969 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
970 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
971 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
972 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
973 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
974 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
975 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
976 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
977 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
978 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
979 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
980 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
981 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
982 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
983 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
984 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
985 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
986 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
987 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
988 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
989 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
993 void LyX::emergencyCleanup() const
995 // what to do about tmpfiles is non-obvious. we would
996 // like to delete any we find, but our lyxdir might
997 // contain documents etc. which might be helpful on
1000 pimpl_->buffer_list_.emergencyWriteAll();
1002 if (pimpl_->lyx_server_)
1003 pimpl_->lyx_server_->emergencyCleanup();
1004 pimpl_->lyx_server_.reset();
1005 pimpl_->lyx_socket_.reset();
1010 void LyX::deadKeyBindings(kb_keymap * kbmap)
1012 // bindKeyings for transparent handling of deadkeys
1013 // The keysyms are gotten from XFree86 X11R6
1014 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1015 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1016 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1017 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1018 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1019 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1020 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1021 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1022 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1023 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1024 // nothing with this name
1025 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1026 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1027 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1028 // nothing with this name either...
1029 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1030 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1031 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1032 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1038 // return true if file does not exist or is older than configure.py.
1039 bool needsUpdate(string const & file)
1041 // We cannot initialize configure_script directly because the package
1042 // is not initialized yet when static objects are constructed.
1043 static string configure_script;
1044 static bool firstrun = true;
1046 configure_script = FileName(addName(
1047 package().system_support(),
1048 "configure.py")).toFilesystemEncoding();
1052 string const absfile = FileName(addName(
1053 package().user_support(), file)).toFilesystemEncoding();
1054 return (! fs::exists(absfile))
1055 || (fs::last_write_time(configure_script)
1056 > fs::last_write_time(absfile));
1062 bool LyX::queryUserLyXDir(bool explicit_userdir)
1064 // Does user directory exist?
1065 string const user_support =
1066 FileName(package().user_support()).toFilesystemEncoding();
1067 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1068 first_start = false;
1070 return needsUpdate("lyxrc.defaults")
1071 || needsUpdate("textclass.lst")
1072 || needsUpdate("packages.lst");
1075 first_start = !explicit_userdir;
1077 // If the user specified explicitly a directory, ask whether
1078 // to create it. If the user says "no", then exit.
1079 if (explicit_userdir &&
1081 _("Missing user LyX directory"),
1082 bformat(_("You have specified a non-existent user "
1083 "LyX directory, %1$s.\n"
1084 "It is needed to keep your own configuration."),
1085 from_utf8(package().user_support())),
1087 _("&Create directory"),
1089 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1090 earlyExit(EXIT_FAILURE);
1093 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1094 from_utf8(package().user_support())))
1097 if (!createDirectory(package().user_support(), 0755)) {
1098 // Failed, so let's exit.
1099 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1101 earlyExit(EXIT_FAILURE);
1108 bool LyX::readRcFile(string const & name)
1110 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1112 FileName const lyxrc_path = libFileSearch(string(), name);
1113 if (!lyxrc_path.empty()) {
1115 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1117 if (lyxrc.read(lyxrc_path) < 0) {
1118 showFileError(name);
1122 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1128 // Read the ui file `name'
1129 bool LyX::readUIFile(string const & name, bool include)
1139 struct keyword_item uitags[ui_last - 1] = {
1140 { "include", ui_include },
1141 { "menuset", ui_menuset },
1142 { "toolbar", ui_toolbar },
1143 { "toolbars", ui_toolbars }
1146 // Ensure that a file is read only once (prevents include loops)
1147 static std::list<string> uifiles;
1148 std::list<string>::const_iterator it = uifiles.begin();
1149 std::list<string>::const_iterator end = uifiles.end();
1150 it = std::find(it, end, name);
1152 lyxerr[Debug::INIT] << "UI file '" << name
1153 << "' has been read already. "
1154 << "Is this an include loop?"
1159 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1164 ui_path = libFileSearch("ui", name, "inc");
1165 if (ui_path.empty())
1166 ui_path = libFileSearch("ui",
1167 changeExtension(name, "inc"));
1170 ui_path = libFileSearch("ui", name, "ui");
1172 if (ui_path.empty()) {
1173 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1174 showFileError(name);
1178 uifiles.push_back(name);
1180 lyxerr[Debug::INIT] << "Found " << name
1181 << " in " << ui_path << endl;
1182 LyXLex lex(uitags, ui_last - 1);
1183 lex.setFile(ui_path);
1185 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1189 if (lyxerr.debugging(Debug::PARSER))
1190 lex.printTable(lyxerr);
1192 while (lex.isOK()) {
1193 switch (lex.lex()) {
1196 string const file = lex.getString();
1197 if (!readUIFile(file, true))
1202 menubackend.read(lex);
1206 toolbarbackend.read(lex);
1210 toolbarbackend.readToolbars(lex);
1214 if (!rtrim(lex.getString()).empty())
1215 lex.printError("LyX::ReadUIFile: "
1216 "Unknown menu tag: `$$Token'");
1224 // Read the languages file `name'
1225 bool LyX::readLanguagesFile(string const & name)
1227 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1229 FileName const lang_path = libFileSearch(string(), name);
1230 if (lang_path.empty()) {
1231 showFileError(name);
1234 languages.read(lang_path);
1239 // Read the encodings file `name'
1240 bool LyX::readEncodingsFile(string const & enc_name,
1241 string const & symbols_name)
1243 lyxerr[Debug::INIT] << "About to read " << enc_name << " and "
1244 << symbols_name << "..." << endl;
1246 FileName const symbols_path = libFileSearch(string(), symbols_name);
1247 if (symbols_path.empty()) {
1248 showFileError(symbols_name);
1252 FileName const enc_path = libFileSearch(string(), enc_name);
1253 if (enc_path.empty()) {
1254 showFileError(enc_name);
1257 encodings.read(enc_path, symbols_path);
1266 /// return the the number of arguments consumed
1267 typedef boost::function<int(string const &, string const &)> cmd_helper;
1269 int parse_dbg(string const & arg, string const &)
1272 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1273 Debug::showTags(lyxerr);
1276 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1278 lyxerr.level(Debug::value(arg));
1279 Debug::showLevel(lyxerr, lyxerr.level());
1284 int parse_help(string const &, string const &)
1287 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1288 "Command line switches (case sensitive):\n"
1289 "\t-help summarize LyX usage\n"
1290 "\t-userdir dir set user directory to dir\n"
1291 "\t-sysdir dir set system directory to dir\n"
1292 "\t-geometry WxH+X+Y set geometry of the main window\n"
1293 "\t-dbg feature[,feature]...\n"
1294 " select the features to debug.\n"
1295 " Type `lyx -dbg' to see the list of features\n"
1296 "\t-x [--execute] command\n"
1297 " where command is a lyx command.\n"
1298 "\t-e [--export] fmt\n"
1299 " where fmt is the export format of choice.\n"
1300 "\t-i [--import] fmt file.xxx\n"
1301 " where fmt is the import format of choice\n"
1302 " and file.xxx is the file to be imported.\n"
1303 "\t-version summarize version and build info\n"
1304 "Check the LyX man page for more details.")) << endl;
1309 int parse_version(string const &, string const &)
1311 lyxerr << "LyX " << lyx_version
1312 << " (" << lyx_release_date << ")" << endl;
1313 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1315 lyxerr << lyx_version_info << endl;
1320 int parse_sysdir(string const & arg, string const &)
1323 Alert::error(_("No system directory"),
1324 _("Missing directory for -sysdir switch"));
1327 cl_system_support = arg;
1331 int parse_userdir(string const & arg, string const &)
1334 Alert::error(_("No user directory"),
1335 _("Missing directory for -userdir switch"));
1338 cl_user_support = arg;
1342 int parse_execute(string const & arg, string const &)
1345 Alert::error(_("Incomplete command"),
1346 _("Missing command string after --execute switch"));
1353 int parse_export(string const & type, string const &)
1356 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1357 "--export switch")) << endl;
1360 batch = "buffer-export " + type;
1365 int parse_import(string const & type, string const & file)
1368 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1369 "--import switch")) << endl;
1373 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1377 batch = "buffer-import " + type + ' ' + file;
1381 int parse_geometry(string const & arg1, string const &)
1384 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1385 // remove also the arg
1388 // don't remove "-geometry"
1397 void LyX::easyParse(int & argc, char * argv[])
1399 std::map<string, cmd_helper> cmdmap;
1401 cmdmap["-dbg"] = parse_dbg;
1402 cmdmap["-help"] = parse_help;
1403 cmdmap["--help"] = parse_help;
1404 cmdmap["-version"] = parse_version;
1405 cmdmap["--version"] = parse_version;
1406 cmdmap["-sysdir"] = parse_sysdir;
1407 cmdmap["-userdir"] = parse_userdir;
1408 cmdmap["-x"] = parse_execute;
1409 cmdmap["--execute"] = parse_execute;
1410 cmdmap["-e"] = parse_export;
1411 cmdmap["--export"] = parse_export;
1412 cmdmap["-i"] = parse_import;
1413 cmdmap["--import"] = parse_import;
1414 cmdmap["-geometry"] = parse_geometry;
1416 for (int i = 1; i < argc; ++i) {
1417 std::map<string, cmd_helper>::const_iterator it
1418 = cmdmap.find(argv[i]);
1420 // don't complain if not found - may be parsed later
1421 if (it == cmdmap.end())
1424 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1425 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1427 int const remove = 1 + it->second(arg, arg2);
1429 // Now, remove used arguments by shifting
1430 // the following ones remove places down.
1433 for (int j = i; j < argc; ++j)
1434 argv[j] = argv[j + remove];
1439 batch_command = batch;
1443 FuncStatus getStatus(FuncRequest const & action)
1445 return LyX::ref().lyxFunc().getStatus(action);
1449 void dispatch(FuncRequest const & action)
1451 LyX::ref().lyxFunc().dispatch(action);
1455 BufferList & theBufferList()
1457 return LyX::ref().bufferList();
1461 LyXFunc & theLyXFunc()
1463 return LyX::ref().lyxFunc();
1467 LyXServer & theLyXServer()
1469 // FIXME: this should not be use_gui dependent
1470 BOOST_ASSERT(use_gui);
1471 return LyX::ref().server();
1475 LyXServerSocket & theLyXServerSocket()
1477 // FIXME: this should not be use_gui dependent
1478 BOOST_ASSERT(use_gui);
1479 return LyX::ref().socket();
1483 kb_keymap & theTopLevelKeymap()
1485 BOOST_ASSERT(use_gui);
1486 return LyX::ref().topLevelKeymap();
1490 Converters & theConverters()
1492 return LyX::ref().converters();
1496 Converters & theSystemConverters()
1498 return LyX::ref().systemConverters();
1502 Movers & theMovers()
1504 return LyX::ref().pimpl_->movers_;
1508 Mover const & getMover(std::string const & fmt)
1510 return LyX::ref().pimpl_->movers_(fmt);
1514 void setMover(std::string const & fmt, std::string const & command)
1516 LyX::ref().pimpl_->movers_.set(fmt, command);
1520 Movers & theSystemMovers()
1522 return LyX::ref().pimpl_->system_movers_;
1526 Messages & getMessages(std::string const & language)
1528 return LyX::ref().getMessages(language);
1532 Messages & getGuiMessages()
1534 return LyX::ref().getGuiMessages();