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/os.h"
57 #include "support/package.h"
58 #include "support/path.h"
59 #include "support/systemcall.h"
60 #include "support/unicode.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::createDirectory;
78 using support::createLyXTmpDir;
79 using support::destroyDir;
80 using support::FileName;
81 using support::fileSearch;
82 using support::getEnv;
83 using support::i18nLibFileSearch;
84 using support::libFileSearch;
85 using support::package;
86 using support::prependEnvPath;
88 using support::Systemcall;
90 namespace Alert = frontend::Alert;
91 namespace os = support::os;
92 namespace fs = boost::filesystem;
101 #ifndef CXX_GLOBAL_CSTD
108 /// are we using the GUI at all?
110 * We default to true and this is changed to false when the export feature is used.
117 // Filled with the command line arguments "foo" of "-sysdir foo" or
119 string cl_system_support;
120 string cl_user_support;
122 std::string geometryArg;
124 LyX * singleton_ = 0;
126 void showFileError(string const & error)
128 Alert::warning(_("Could not read configuration file"),
129 bformat(_("Error while reading the configuration file\n%1$s.\n"
130 "Please check your installation."), from_utf8(error)));
134 void reconfigureUserLyXDir()
136 string const configure_command = package().configure_command();
138 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
139 support::Path p(package().user_support());
141 one.startscript(Systemcall::Wait, configure_command);
142 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
148 /// The main application class private implementation.
149 struct LyX::Singletons
151 Singletons(): iconv(ucs4_codeset, "UTF-8")
154 /// our function handler
157 BufferList buffer_list_;
159 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
161 boost::scoped_ptr<LyXServer> lyx_server_;
163 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
165 boost::scoped_ptr<frontend::Application> application_;
166 /// lyx session, containing lastfiles, lastfilepos, and lastopened
167 boost::scoped_ptr<Session> session_;
170 IconvProcessor iconv;
172 /// Files to load at start.
173 vector<FileName> files_to_load_;
176 map<string, Messages> messages_;
180 frontend::Application * theApp()
183 return &singleton_->application();
196 BOOST_ASSERT(singleton_);
201 LyX const & LyX::cref()
203 BOOST_ASSERT(singleton_);
212 pimpl_.reset(new Singletons);
217 BufferList & LyX::bufferList()
219 return pimpl_->buffer_list_;
223 BufferList const & LyX::bufferList() const
225 return pimpl_->buffer_list_;
229 Session & LyX::session()
231 BOOST_ASSERT(pimpl_->session_.get());
232 return *pimpl_->session_.get();
236 Session const & LyX::session() const
238 BOOST_ASSERT(pimpl_->session_.get());
239 return *pimpl_->session_.get();
243 LyXFunc & LyX::lyxFunc()
245 return pimpl_->lyxfunc_;
249 LyXFunc const & LyX::lyxFunc() const
251 return pimpl_->lyxfunc_;
255 LyXServer & LyX::server()
257 BOOST_ASSERT(pimpl_->lyx_server_.get());
258 return *pimpl_->lyx_server_.get();
262 LyXServer const & LyX::server() const
264 BOOST_ASSERT(pimpl_->lyx_server_.get());
265 return *pimpl_->lyx_server_.get();
269 LyXServerSocket & LyX::socket()
271 BOOST_ASSERT(pimpl_->lyx_socket_.get());
272 return *pimpl_->lyx_socket_.get();
276 LyXServerSocket const & LyX::socket() const
278 BOOST_ASSERT(pimpl_->lyx_socket_.get());
279 return *pimpl_->lyx_socket_.get();
283 frontend::Application & LyX::application()
285 BOOST_ASSERT(pimpl_->application_.get());
286 return *pimpl_->application_.get();
290 frontend::Application const & LyX::application() const
292 BOOST_ASSERT(pimpl_->application_.get());
293 return *pimpl_->application_.get();
297 kb_keymap & LyX::topLevelKeymap()
299 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
300 return *pimpl_->toplevel_keymap_.get();
304 IconvProcessor & LyX::iconvProcessor()
306 return pimpl_->iconv;
310 kb_keymap const & LyX::topLevelKeymap() const
312 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
313 return *pimpl_->toplevel_keymap_.get();
317 Messages & LyX::getMessages(std::string const & language)
319 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
321 if (it != pimpl_->messages_.end())
324 std::pair<map<string, Messages>::iterator, bool> result =
325 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
327 BOOST_ASSERT(result.second);
328 return result.first->second;
332 Messages & LyX::getGuiMessages()
334 return pimpl_->messages_["GUI"];
338 void LyX::setGuiLanguage(std::string const & language)
340 pimpl_->messages_["GUI"] = Messages(language);
344 Buffer const * const LyX::updateInset(InsetBase const * inset) const
349 Buffer const * buffer_ptr = 0;
350 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
351 vector<int>::const_iterator it = view_ids.begin();
352 vector<int>::const_iterator const end = view_ids.end();
353 for (; it != end; ++it) {
355 pimpl_->application_->gui().view(*it).updateInset(inset);
363 int LyX::exec(int & argc, char * argv[])
365 // Here we need to parse the command line. At least
366 // we need to parse for "-dbg" and "-help"
367 easyParse(argc, argv);
369 support::init_package(to_utf8(from_local8bit(argv[0])),
370 cl_system_support, cl_user_support,
371 support::top_build_dir_is_one_level_up);
374 // FIXME: create a ConsoleApplication
375 int exit_status = init(argc, argv);
383 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
388 BufferList::iterator begin = pimpl_->buffer_list_.begin();
389 BufferList::iterator end = pimpl_->buffer_list_.end();
391 bool final_success = false;
392 for (BufferList::iterator I = begin; I != end; ++I) {
394 bool success = false;
395 buf->dispatch(batch_command, &success);
396 final_success |= success;
399 return !final_success;
402 // Force adding of font path _before_ Application is initialized
403 support::os::addFontResources();
405 // Let the frontend parse and remove all arguments that it knows
406 pimpl_->application_.reset(createApplication(argc, argv));
410 // Parse and remove all known arguments in the LyX singleton
411 // Give an error for all remaining ones.
412 int exit_status = init(argc, argv);
414 // Kill the application object before exiting.
415 pimpl_->application_.reset();
422 /* Create a CoreApplication class that will provide the main event loop
423 * and the socket callback registering. With Qt4, only QtCore
424 * library would be needed.
425 * When this is done, a server_mode could be created and the following two
426 * line would be moved out from here.
428 // Note: socket callback must be registered after init(argc, argv)
429 // such that package().temp_dir() is properly initialized.
430 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
431 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
432 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
434 // Start the real execution loop.
435 exit_status = pimpl_->application_->exec();
439 // Restore original font resources after Application is destroyed.
440 support::os::restoreFontResources();
446 void LyX::prepareExit()
448 // Set a flag that we do quitting from the program,
449 // so no refreshes are necessary.
452 // close buffers first
453 pimpl_->buffer_list_.closeAll();
455 // do any other cleanup procedures now
456 if (package().temp_dir() != package().system_temp_dir()) {
457 lyxerr[Debug::INFO] << "Deleting tmp dir "
458 << package().temp_dir() << endl;
460 if (!destroyDir(FileName(package().temp_dir()))) {
461 docstring const msg =
462 bformat(_("Unable to remove the temporary directory %1$s"),
463 from_utf8(package().temp_dir()));
464 Alert::warning(_("Unable to remove temporary directory"), msg);
469 if (pimpl_->session_)
470 pimpl_->session_->writeFile();
471 pimpl_->session_.reset();
472 pimpl_->lyx_server_.reset();
473 pimpl_->lyx_socket_.reset();
476 // Kill the application object before exiting. This avoids crashes
477 // when exiting on Linux.
478 if (pimpl_->application_)
479 pimpl_->application_.reset();
483 void LyX::earlyExit(int status)
485 BOOST_ASSERT(pimpl_->application_.get());
486 // LyX::pimpl_::application_ is not initialised at this
487 // point so it's safe to just exit after some cleanup.
493 int LyX::init(int & argc, char * argv[])
495 // check for any spurious extra arguments
496 // other than documents
497 for (int argi = 1; argi < argc ; ++argi) {
498 if (argv[argi][0] == '-') {
500 bformat(_("Wrong command line option `%1$s'. Exiting."),
501 from_utf8(argv[argi]))) << endl;
506 // Initialization of LyX (reads lyxrc and more)
507 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
508 bool success = init();
509 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
513 for (int argi = argc - 1; argi >= 1; --argi) {
514 // get absolute path of file and add ".lyx" to
515 // the filename if necessary
516 pimpl_->files_to_load_.push_back(fileSearch(string(),
517 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
518 "lyx", support::allow_unreadable));
522 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
528 void LyX::loadFiles()
530 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
531 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
533 for (; it != end; ++it) {
537 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
538 if (loadLyXFile(buf, *it)) {
539 ErrorList const & el = buf->errorList("Parse");
541 for_each(el.begin(), el.end(),
542 boost::bind(&LyX::printError, this, _1));
545 pimpl_->buffer_list_.release(buf);
550 void LyX::execBatchCommands()
552 // The advantage of doing this here is that the event loop
553 // is already started. So any need for interaction will be
557 // Execute batch commands if available
558 if (batch_command.empty())
561 lyxerr[Debug::INIT] << "About to handle -x '"
562 << batch_command << '\'' << endl;
564 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
568 void LyX::restoreGuiSession()
570 LyXView * view = newLyXView();
572 // if some files were specified at command-line we assume that the
573 // user wants to edit *these* files and not to restore the session.
574 if (!pimpl_->files_to_load_.empty()) {
575 for_each(pimpl_->files_to_load_.begin(),
576 pimpl_->files_to_load_.end(),
577 bind(&LyXView::loadLyXFile, view, _1, true));
578 // clear this list to save a few bytes of RAM
579 pimpl_->files_to_load_.clear();
580 pimpl_->session_->lastOpened().clear();
584 if (!lyxrc.load_session)
587 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
588 // do not add to the lastfile list since these files are restored from
589 // last session, and should be already there (regular files), or should
590 // not be added at all (help files).
591 for_each(lastopened.begin(), lastopened.end(),
592 bind(&LyXView::loadLyXFile, view, _1, false));
594 // clear this list to save a few bytes of RAM
595 pimpl_->session_->lastOpened().clear();
599 LyXView * LyX::newLyXView()
604 // determine windows size and position, from lyxrc and/or session
606 unsigned int width = 690;
607 unsigned int height = 510;
608 // default icon size, will be overwritten by stored session value
609 unsigned int iconSizeXY = 0;
610 bool maximize = false;
612 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
613 width = lyxrc.geometry_width;
614 height = lyxrc.geometry_height;
616 // if lyxrc returns (0,0), then use session info
618 string val = session().sessionInfo().load("WindowWidth");
620 width = convert<unsigned int>(val);
621 val = session().sessionInfo().load("WindowHeight");
623 height = convert<unsigned int>(val);
624 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
626 val = session().sessionInfo().load("IconSizeXY");
628 iconSizeXY = convert<unsigned int>(val);
631 // if user wants to restore window position
634 if (lyxrc.geometry_xysaved) {
635 string val = session().sessionInfo().load("WindowPosX");
637 posx = convert<int>(val);
638 val = session().sessionInfo().load("WindowPosY");
640 posy = convert<int>(val);
643 if (!geometryArg.empty())
649 // create the main window
650 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
658 The SIGHUP signal does not exist on Windows and does not need to be handled.
660 Windows handles SIGFPE and SIGSEGV signals as expected.
662 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
663 cause a new thread to be spawned. This may well result in unexpected
664 behaviour by the single-threaded LyX.
666 SIGTERM signals will come only from another process actually sending
667 that signal using 'raise' in Windows' POSIX compatability layer. It will
668 not come from the general "terminate process" methods that everyone
669 actually uses (and which can't be trapped). Killing an app 'politely' on
670 Windows involves first sending a WM_CLOSE message, something that is
671 caught already by the Qt frontend.
673 For more information see:
675 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
676 ...signals are mostly useless on Windows for a variety of reasons that are
679 'UNIX Application Migration Guide, Chapter 9'
680 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
682 'How To Terminate an Application "Cleanly" in Win32'
683 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
687 static void error_handler(int err_sig)
689 // Throw away any signals other than the first one received.
690 static sig_atomic_t handling_error = false;
693 handling_error = true;
695 // We have received a signal indicating a fatal error, so
696 // try and save the data ASAP.
697 LyX::cref().emergencyCleanup();
699 // These lyxerr calls may or may not work:
701 // Signals are asynchronous, so the main program may be in a very
702 // fragile state when a signal is processed and thus while a signal
703 // handler function executes.
704 // In general, therefore, we should avoid performing any
705 // I/O operations or calling most library and system functions from
708 // This shouldn't matter here, however, as we've already invoked
713 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
717 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
720 lyxerr << "\nlyx: SIGSEGV signal caught\n"
721 "Sorry, you have found a bug in LyX. "
722 "Please read the bug-reporting instructions "
723 "in Help->Introduction and send us a bug report, "
724 "if necessary. Thanks !\nBye." << endl;
732 // Deinstall the signal handlers
734 signal(SIGHUP, SIG_DFL);
736 signal(SIGINT, SIG_DFL);
737 signal(SIGFPE, SIG_DFL);
738 signal(SIGSEGV, SIG_DFL);
739 signal(SIGTERM, SIG_DFL);
742 if (err_sig == SIGSEGV ||
743 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
745 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
754 void LyX::printError(ErrorItem const & ei)
756 docstring tmp = _("LyX: ") + ei.error + char_type(':')
758 std::cerr << to_utf8(tmp) << std::endl;
762 void LyX::initGuiFont()
764 if (lyxrc.roman_font_name.empty())
765 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
767 if (lyxrc.sans_font_name.empty())
768 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
770 if (lyxrc.typewriter_font_name.empty())
771 lyxrc.typewriter_font_name
772 = pimpl_->application_->typewriterFontName();
779 signal(SIGHUP, error_handler);
781 signal(SIGFPE, error_handler);
782 signal(SIGSEGV, error_handler);
783 signal(SIGINT, error_handler);
784 signal(SIGTERM, error_handler);
785 // SIGPIPE can be safely ignored.
787 lyxrc.tempdir_path = package().temp_dir();
788 lyxrc.document_path = package().document_dir();
790 if (lyxrc.template_path.empty()) {
791 lyxrc.template_path = addPath(package().system_support(),
796 // Read configuration files
799 // This one may have been distributed along with LyX.
800 if (!readRcFile("lyxrc.dist"))
803 // Set the User Interface language.
804 pimpl_->messages_["GUI"] = Messages();
806 // Set the PATH correctly.
807 #if !defined (USE_POSIX_PACKAGING)
808 // Add the directory containing the LyX executable to the path
809 // so that LyX can find things like tex2lyx.
810 if (package().build_support().empty())
811 prependEnvPath("PATH", package().binary_dir());
813 if (!lyxrc.path_prefix.empty())
814 prependEnvPath("PATH", lyxrc.path_prefix);
816 // Check that user LyX directory is ok.
817 if (queryUserLyXDir(package().explicit_user_support()))
818 reconfigureUserLyXDir();
820 // no need for a splash when there is no GUI
825 // This one is generated in user_support directory by lib/configure.py.
826 if (!readRcFile("lyxrc.defaults"))
829 // Query the OS to know what formats are viewed natively
830 formats.setAutoOpen();
832 // Read lyxrc.dist again to be able to override viewer auto-detection.
833 readRcFile("lyxrc.dist");
835 system_lyxrc = lyxrc;
836 system_formats = formats;
837 system_converters = converters;
838 system_movers = movers;
839 system_lcolor = lcolor;
841 // This one is edited through the preferences dialog.
842 if (!readRcFile("preferences"))
845 if (!readEncodingsFile("encodings"))
847 if (!readLanguagesFile("languages"))
851 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
857 pimpl_->toplevel_keymap_.reset(new kb_keymap);
858 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
859 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
861 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
864 if (!readUIFile(lyxrc.ui_file))
868 if (lyxerr.debugging(Debug::LYXRC))
871 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
872 if (!lyxrc.path_prefix.empty())
873 prependEnvPath("PATH", lyxrc.path_prefix);
875 FileName const document_path(lyxrc.document_path);
876 if (fs::exists(document_path.toFilesystemEncoding()) &&
877 fs::is_directory(document_path.toFilesystemEncoding()))
878 package().document_dir() = lyxrc.document_path;
880 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
881 if (package().temp_dir().empty()) {
882 Alert::error(_("Could not create temporary directory"),
883 bformat(_("Could not create a temporary directory in\n"
884 "%1$s. Make sure that this\n"
885 "path exists and is writable and try again."),
886 from_utf8(lyxrc.tempdir_path)));
887 // createLyXTmpDir() tries sufficiently hard to create a
888 // usable temp dir, so the probability to come here is
889 // close to zero. We therefore don't try to overcome this
890 // problem with e.g. asking the user for a new path and
891 // trying again but simply exit.
895 if (lyxerr.debugging(Debug::INIT)) {
896 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
899 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
900 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
902 // This must happen after package initialization and after lyxrc is
903 // read, therefore it can't be done by a static object.
904 ConverterCache::init();
910 void LyX::defaultKeyBindings(kb_keymap * kbmap)
912 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
913 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
914 kbmap->bind("Up", FuncRequest(LFUN_UP));
915 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
917 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
918 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
919 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
920 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
922 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
923 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
924 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
925 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
927 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
928 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
930 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
931 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
933 // kbmap->bindings to enable the use of the numeric keypad
935 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
936 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
937 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
938 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
939 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
940 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
941 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
942 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
943 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
944 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
945 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
946 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
947 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
948 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
949 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
950 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
951 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
952 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
953 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
954 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
955 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
956 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
957 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
958 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
962 void LyX::emergencyCleanup() const
964 // what to do about tmpfiles is non-obvious. we would
965 // like to delete any we find, but our lyxdir might
966 // contain documents etc. which might be helpful on
969 pimpl_->buffer_list_.emergencyWriteAll();
971 if (pimpl_->lyx_server_)
972 pimpl_->lyx_server_->emergencyCleanup();
973 pimpl_->lyx_server_.reset();
974 pimpl_->lyx_socket_.reset();
979 void LyX::deadKeyBindings(kb_keymap * kbmap)
981 // bindKeyings for transparent handling of deadkeys
982 // The keysyms are gotten from XFree86 X11R6
983 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
984 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
985 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
986 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
987 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
988 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
989 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
990 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
991 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
992 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
993 // nothing with this name
994 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
995 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
996 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
997 // nothing with this name either...
998 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
999 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1000 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1001 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1007 // return true if file does not exist or is older than configure.py.
1008 bool needsUpdate(string const & file)
1010 // We cannot initialize configure_script directly because the package
1011 // is not initialized yet when static objects are constructed.
1012 static string configure_script;
1013 static bool firstrun = true;
1015 configure_script = FileName(addName(
1016 package().system_support(),
1017 "configure.py")).toFilesystemEncoding();
1021 string const absfile = FileName(addName(
1022 package().user_support(), file)).toFilesystemEncoding();
1023 return (! fs::exists(absfile))
1024 || (fs::last_write_time(configure_script)
1025 > fs::last_write_time(absfile));
1031 bool LyX::queryUserLyXDir(bool explicit_userdir)
1033 // Does user directory exist?
1034 string const user_support =
1035 FileName(package().user_support()).toFilesystemEncoding();
1036 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1037 first_start = false;
1039 return needsUpdate("lyxrc.defaults")
1040 || needsUpdate("textclass.lst")
1041 || needsUpdate("packages.lst");
1044 first_start = !explicit_userdir;
1046 // If the user specified explicitly a directory, ask whether
1047 // to create it. If the user says "no", then exit.
1048 if (explicit_userdir &&
1050 _("Missing user LyX directory"),
1051 bformat(_("You have specified a non-existent user "
1052 "LyX directory, %1$s.\n"
1053 "It is needed to keep your own configuration."),
1054 from_utf8(package().user_support())),
1056 _("&Create directory"),
1058 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1059 earlyExit(EXIT_FAILURE);
1062 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1063 from_utf8(package().user_support())))
1066 if (!createDirectory(package().user_support(), 0755)) {
1067 // Failed, so let's exit.
1068 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1070 earlyExit(EXIT_FAILURE);
1077 bool LyX::readRcFile(string const & name)
1079 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1081 FileName const lyxrc_path = libFileSearch(string(), name);
1082 if (!lyxrc_path.empty()) {
1084 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1086 if (lyxrc.read(lyxrc_path) < 0) {
1087 showFileError(name);
1091 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1097 // Read the ui file `name'
1098 bool LyX::readUIFile(string const & name)
1108 struct keyword_item uitags[ui_last - 1] = {
1109 { "include", ui_include },
1110 { "menuset", ui_menuset },
1111 { "toolbar", ui_toolbar },
1112 { "toolbars", ui_toolbars }
1115 // Ensure that a file is read only once (prevents include loops)
1116 static std::list<string> uifiles;
1117 std::list<string>::const_iterator it = uifiles.begin();
1118 std::list<string>::const_iterator end = uifiles.end();
1119 it = std::find(it, end, name);
1121 lyxerr[Debug::INIT] << "UI file '" << name
1122 << "' has been read already. "
1123 << "Is this an include loop?"
1128 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1130 FileName const ui_path = libFileSearch("ui", name, "ui");
1132 if (ui_path.empty()) {
1133 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1134 showFileError(name);
1137 uifiles.push_back(name);
1139 lyxerr[Debug::INIT] << "Found " << name
1140 << " in " << ui_path << endl;
1141 LyXLex lex(uitags, ui_last - 1);
1142 lex.setFile(ui_path);
1144 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1148 if (lyxerr.debugging(Debug::PARSER))
1149 lex.printTable(lyxerr);
1151 while (lex.isOK()) {
1152 switch (lex.lex()) {
1155 string const file = lex.getString();
1156 if (!readUIFile(file))
1161 menubackend.read(lex);
1165 toolbarbackend.read(lex);
1169 toolbarbackend.readToolbars(lex);
1173 if (!rtrim(lex.getString()).empty())
1174 lex.printError("LyX::ReadUIFile: "
1175 "Unknown menu tag: `$$Token'");
1183 // Read the languages file `name'
1184 bool LyX::readLanguagesFile(string const & name)
1186 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1188 FileName const lang_path = libFileSearch(string(), name);
1189 if (lang_path.empty()) {
1190 showFileError(name);
1193 languages.read(lang_path);
1198 // Read the encodings file `name'
1199 bool LyX::readEncodingsFile(string const & name)
1201 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1203 FileName const enc_path = libFileSearch(string(), name);
1204 if (enc_path.empty()) {
1205 showFileError(name);
1208 encodings.read(enc_path);
1217 /// return the the number of arguments consumed
1218 typedef boost::function<int(string const &, string const &)> cmd_helper;
1220 int parse_dbg(string const & arg, string const &)
1223 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1224 Debug::showTags(lyxerr);
1227 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1229 lyxerr.level(Debug::value(arg));
1230 Debug::showLevel(lyxerr, lyxerr.level());
1235 int parse_help(string const &, string const &)
1238 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1239 "Command line switches (case sensitive):\n"
1240 "\t-help summarize LyX usage\n"
1241 "\t-userdir dir set user directory to dir\n"
1242 "\t-sysdir dir set system directory to dir\n"
1243 "\t-geometry WxH+X+Y set geometry of the main window\n"
1244 "\t-dbg feature[,feature]...\n"
1245 " select the features to debug.\n"
1246 " Type `lyx -dbg' to see the list of features\n"
1247 "\t-x [--execute] command\n"
1248 " where command is a lyx command.\n"
1249 "\t-e [--export] fmt\n"
1250 " where fmt is the export format of choice.\n"
1251 "\t-i [--import] fmt file.xxx\n"
1252 " where fmt is the import format of choice\n"
1253 " and file.xxx is the file to be imported.\n"
1254 "\t-version summarize version and build info\n"
1255 "Check the LyX man page for more details.")) << endl;
1260 int parse_version(string const &, string const &)
1262 lyxerr << "LyX " << lyx_version
1263 << " (" << lyx_release_date << ")" << endl;
1264 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1266 lyxerr << lyx_version_info << endl;
1271 int parse_sysdir(string const & arg, string const &)
1274 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1277 cl_system_support = arg;
1281 int parse_userdir(string const & arg, string const &)
1284 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1287 cl_user_support = arg;
1291 int parse_execute(string const & arg, string const &)
1294 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1301 int parse_export(string const & type, string const &)
1304 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1305 "--export switch")) << endl;
1308 batch = "buffer-export " + type;
1313 int parse_import(string const & type, string const & file)
1316 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1317 "--import switch")) << endl;
1321 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1325 batch = "buffer-import " + type + ' ' + file;
1329 int parse_geometry(string const & arg1, string const &)
1332 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1333 // remove also the arg
1336 // don't remove "-geometry"
1345 void LyX::easyParse(int & argc, char * argv[])
1347 std::map<string, cmd_helper> cmdmap;
1349 cmdmap["-dbg"] = parse_dbg;
1350 cmdmap["-help"] = parse_help;
1351 cmdmap["--help"] = parse_help;
1352 cmdmap["-version"] = parse_version;
1353 cmdmap["--version"] = parse_version;
1354 cmdmap["-sysdir"] = parse_sysdir;
1355 cmdmap["-userdir"] = parse_userdir;
1356 cmdmap["-x"] = parse_execute;
1357 cmdmap["--execute"] = parse_execute;
1358 cmdmap["-e"] = parse_export;
1359 cmdmap["--export"] = parse_export;
1360 cmdmap["-i"] = parse_import;
1361 cmdmap["--import"] = parse_import;
1362 cmdmap["-geometry"] = parse_geometry;
1364 for (int i = 1; i < argc; ++i) {
1365 std::map<string, cmd_helper>::const_iterator it
1366 = cmdmap.find(argv[i]);
1368 // don't complain if not found - may be parsed later
1369 if (it == cmdmap.end())
1372 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1373 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1375 int const remove = 1 + it->second(arg, arg2);
1377 // Now, remove used arguments by shifting
1378 // the following ones remove places down.
1381 for (int j = i; j < argc; ++j)
1382 argv[j] = argv[j + remove];
1387 batch_command = batch;
1391 FuncStatus getStatus(FuncRequest const & action)
1393 return LyX::ref().lyxFunc().getStatus(action);
1397 void dispatch(FuncRequest const & action)
1399 LyX::ref().lyxFunc().dispatch(action);
1403 BufferList & theBufferList()
1405 return LyX::ref().bufferList();
1409 LyXFunc & theLyXFunc()
1411 return LyX::ref().lyxFunc();
1415 LyXServer & theLyXServer()
1417 // FIXME: this should not be use_gui dependent
1418 BOOST_ASSERT(use_gui);
1419 return LyX::ref().server();
1423 LyXServerSocket & theLyXServerSocket()
1425 // FIXME: this should not be use_gui dependent
1426 BOOST_ASSERT(use_gui);
1427 return LyX::ref().socket();
1431 kb_keymap & theTopLevelKeymap()
1433 BOOST_ASSERT(use_gui);
1434 return LyX::ref().topLevelKeymap();
1438 IconvProcessor & utf8ToUcs4()
1440 return LyX::ref().iconvProcessor();
1444 Messages & getMessages(std::string const & language)
1446 return LyX::ref().getMessages(language);
1450 Messages & getGuiMessages()
1452 return LyX::ref().getGuiMessages();