3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
20 #include "ConverterCache.h"
22 #include "buffer_funcs.h"
23 #include "BufferList.h"
24 #include "Converter.h"
27 #include "ErrorList.h"
35 #include "LyXAction.h"
40 #include "ServerSocket.h"
41 #include "TextClassList.h"
42 #include "MenuBackend.h"
45 #include "ToolbarBackend.h"
47 #include "frontends/alert.h"
48 #include "frontends/Application.h"
49 #include "frontends/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>
75 using support::addName;
76 using support::addPath;
77 using support::bformat;
78 using support::changeExtension;
79 using support::createDirectory;
80 using support::createLyXTmpDir;
81 using support::destroyDir;
82 using support::FileName;
83 using support::fileSearch;
84 using support::getEnv;
85 using support::i18nLibFileSearch;
86 using support::libFileSearch;
87 using support::package;
88 using support::prependEnvPath;
90 using support::Systemcall;
92 namespace Alert = frontend::Alert;
93 namespace os = support::os;
94 namespace fs = boost::filesystem;
103 #ifndef CXX_GLOBAL_CSTD
110 /// are we using the GUI at all?
112 * We default to true and this is changed to false when the export feature is used.
119 // Filled with the command line arguments "foo" of "-sysdir foo" or
121 string cl_system_support;
122 string cl_user_support;
124 std::string geometryArg;
126 LyX * singleton_ = 0;
128 void showFileError(string const & error)
130 Alert::warning(_("Could not read configuration file"),
131 bformat(_("Error while reading the configuration file\n%1$s.\n"
132 "Please check your installation."), from_utf8(error)));
136 void reconfigureUserLyXDir()
138 string const configure_command = package().configure_command();
140 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
141 support::Path p(package().user_support());
143 one.startscript(Systemcall::Wait, configure_command);
144 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
150 /// The main application class private implementation.
151 struct LyX::Singletons
155 // Set the default User Interface language as soon as possible.
156 // The language used will be derived from the environment
158 messages_["GUI"] = Messages();
160 /// our function handler
163 BufferList buffer_list_;
165 boost::scoped_ptr<KeyMap> toplevel_keymap_;
167 boost::scoped_ptr<Server> lyx_server_;
169 boost::scoped_ptr<ServerSocket> lyx_socket_;
171 boost::scoped_ptr<frontend::Application> application_;
172 /// lyx session, containing lastfiles, lastfilepos, and lastopened
173 boost::scoped_ptr<Session> session_;
175 /// Files to load at start.
176 vector<FileName> files_to_load_;
178 /// The messages translators.
179 map<string, Messages> messages_;
181 /// The file converters.
182 Converters converters_;
184 // The system converters copy after reading lyxrc.defaults.
185 Converters system_converters_;
191 Movers system_movers_;
195 frontend::Application * theApp()
198 return singleton_->pimpl_->application_.get();
211 BOOST_ASSERT(singleton_);
216 LyX const & LyX::cref()
218 BOOST_ASSERT(singleton_);
227 pimpl_.reset(new Singletons);
231 BufferList & LyX::bufferList()
233 return pimpl_->buffer_list_;
237 BufferList const & LyX::bufferList() const
239 return pimpl_->buffer_list_;
243 Session & LyX::session()
245 BOOST_ASSERT(pimpl_->session_.get());
246 return *pimpl_->session_.get();
250 Session const & LyX::session() const
252 BOOST_ASSERT(pimpl_->session_.get());
253 return *pimpl_->session_.get();
257 LyXFunc & LyX::lyxFunc()
259 return pimpl_->lyxfunc_;
263 LyXFunc const & LyX::lyxFunc() const
265 return pimpl_->lyxfunc_;
269 Server & LyX::server()
271 BOOST_ASSERT(pimpl_->lyx_server_.get());
272 return *pimpl_->lyx_server_.get();
276 Server const & LyX::server() const
278 BOOST_ASSERT(pimpl_->lyx_server_.get());
279 return *pimpl_->lyx_server_.get();
283 ServerSocket & LyX::socket()
285 BOOST_ASSERT(pimpl_->lyx_socket_.get());
286 return *pimpl_->lyx_socket_.get();
290 ServerSocket const & LyX::socket() const
292 BOOST_ASSERT(pimpl_->lyx_socket_.get());
293 return *pimpl_->lyx_socket_.get();
297 frontend::Application & LyX::application()
299 BOOST_ASSERT(pimpl_->application_.get());
300 return *pimpl_->application_.get();
304 frontend::Application const & LyX::application() const
306 BOOST_ASSERT(pimpl_->application_.get());
307 return *pimpl_->application_.get();
311 KeyMap & LyX::topLevelKeymap()
313 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
314 return *pimpl_->toplevel_keymap_.get();
318 Converters & LyX::converters()
320 return pimpl_->converters_;
324 Converters & LyX::systemConverters()
326 return pimpl_->system_converters_;
330 KeyMap const & LyX::topLevelKeymap() const
332 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
333 return *pimpl_->toplevel_keymap_.get();
337 Messages & LyX::getMessages(std::string const & language)
339 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
341 if (it != pimpl_->messages_.end())
344 std::pair<map<string, Messages>::iterator, bool> result =
345 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
347 BOOST_ASSERT(result.second);
348 return result.first->second;
352 Messages & LyX::getGuiMessages()
354 return pimpl_->messages_["GUI"];
358 void LyX::setGuiLanguage(std::string const & language)
360 pimpl_->messages_["GUI"] = Messages(language);
364 Buffer const * const LyX::updateInset(Inset const * inset) const
369 Buffer const * buffer_ptr = 0;
370 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
371 vector<int>::const_iterator it = view_ids.begin();
372 vector<int>::const_iterator const end = view_ids.end();
373 for (; it != end; ++it) {
375 pimpl_->application_->gui().view(*it).updateInset(inset);
383 int LyX::exec(int & argc, char * argv[])
385 // Here we need to parse the command line. At least
386 // we need to parse for "-dbg" and "-help"
387 easyParse(argc, argv);
389 try { support::init_package(to_utf8(from_local8bit(argv[0])),
390 cl_system_support, cl_user_support,
391 support::top_build_dir_is_one_level_up);
392 } catch (support::ExceptionMessage const & message) {
393 if (message.type_ == support::ErrorException) {
394 Alert::error(message.title_, message.details_);
396 } else if (message.type_ == support::WarningException) {
397 Alert::warning(message.title_, message.details_);
402 // FIXME: create a ConsoleApplication
403 int exit_status = init(argc, argv);
411 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
416 BufferList::iterator begin = pimpl_->buffer_list_.begin();
417 BufferList::iterator end = pimpl_->buffer_list_.end();
419 bool final_success = false;
420 for (BufferList::iterator I = begin; I != end; ++I) {
422 bool success = false;
423 buf->dispatch(batch_command, &success);
424 final_success |= success;
427 return !final_success;
430 // Force adding of font path _before_ Application is initialized
431 support::os::addFontResources();
433 // Let the frontend parse and remove all arguments that it knows
434 pimpl_->application_.reset(createApplication(argc, argv));
438 // Parse and remove all known arguments in the LyX singleton
439 // Give an error for all remaining ones.
440 int exit_status = init(argc, argv);
442 // Kill the application object before exiting.
443 pimpl_->application_.reset();
450 /* Create a CoreApplication class that will provide the main event loop
451 * and the socket callback registering. With Qt4, only QtCore
452 * library would be needed.
453 * When this is done, a server_mode could be created and the following two
454 * line would be moved out from here.
456 // Note: socket callback must be registered after init(argc, argv)
457 // such that package().temp_dir() is properly initialized.
458 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
459 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
460 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
462 // Start the real execution loop.
463 exit_status = pimpl_->application_->exec();
467 // Restore original font resources after Application is destroyed.
468 support::os::restoreFontResources();
474 void LyX::prepareExit()
476 // Set a flag that we do quitting from the program,
477 // so no refreshes are necessary.
480 // close buffers first
481 pimpl_->buffer_list_.closeAll();
483 // do any other cleanup procedures now
484 if (package().temp_dir() != package().system_temp_dir()) {
485 LYXERR(Debug::INFO) << "Deleting tmp dir "
486 << package().temp_dir().absFilename() << endl;
488 if (!destroyDir(package().temp_dir())) {
489 docstring const msg =
490 bformat(_("Unable to remove the temporary directory %1$s"),
491 from_utf8(package().temp_dir().absFilename()));
492 Alert::warning(_("Unable to remove temporary directory"), msg);
497 if (pimpl_->session_)
498 pimpl_->session_->writeFile();
499 pimpl_->session_.reset();
500 pimpl_->lyx_server_.reset();
501 pimpl_->lyx_socket_.reset();
504 // Kill the application object before exiting. This avoids crashes
505 // when exiting on Linux.
506 if (pimpl_->application_)
507 pimpl_->application_.reset();
511 void LyX::earlyExit(int status)
513 BOOST_ASSERT(pimpl_->application_.get());
514 // LyX::pimpl_::application_ is not initialised at this
515 // point so it's safe to just exit after some cleanup.
521 int LyX::init(int & argc, char * argv[])
523 // check for any spurious extra arguments
524 // other than documents
525 for (int argi = 1; argi < argc ; ++argi) {
526 if (argv[argi][0] == '-') {
528 bformat(_("Wrong command line option `%1$s'. Exiting."),
529 from_utf8(argv[argi]))) << endl;
534 // Initialization of LyX (reads lyxrc and more)
535 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
536 bool success = init();
537 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
541 for (int argi = argc - 1; argi >= 1; --argi) {
542 // get absolute path of file and add ".lyx" to
543 // the filename if necessary
544 pimpl_->files_to_load_.push_back(fileSearch(string(),
545 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
546 "lyx", support::allow_unreadable));
550 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
556 void LyX::addFileToLoad(FileName const & fname)
558 vector<FileName>::const_iterator cit = std::find(
559 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
562 if (cit == pimpl_->files_to_load_.end())
563 pimpl_->files_to_load_.push_back(fname);
567 void LyX::loadFiles()
569 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
570 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
572 for (; it != end; ++it) {
576 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
577 if (loadLyXFile(buf, *it)) {
578 ErrorList const & el = buf->errorList("Parse");
580 for_each(el.begin(), el.end(),
581 boost::bind(&LyX::printError, this, _1));
584 pimpl_->buffer_list_.release(buf);
589 void LyX::execBatchCommands()
591 // The advantage of doing this here is that the event loop
592 // is already started. So any need for interaction will be
596 // Execute batch commands if available
597 if (batch_command.empty())
600 LYXERR(Debug::INIT) << "About to handle -x '"
601 << batch_command << '\'' << endl;
603 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
607 void LyX::restoreGuiSession()
609 LyXView * view = newLyXView();
611 // if some files were specified at command-line we assume that the
612 // user wants to edit *these* files and not to restore the session.
613 if (!pimpl_->files_to_load_.empty()) {
614 for_each(pimpl_->files_to_load_.begin(),
615 pimpl_->files_to_load_.end(),
616 bind(&LyXView::loadLyXFile, view, _1, true));
617 // clear this list to save a few bytes of RAM
618 pimpl_->files_to_load_.clear();
619 pimpl_->session_->lastOpened().clear();
623 if (!lyxrc.load_session)
626 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
627 // do not add to the lastfile list since these files are restored from
628 // last session, and should be already there (regular files), or should
629 // not be added at all (help files).
630 for_each(lastopened.begin(), lastopened.end(),
631 bind(&LyXView::loadLyXFile, view, _1, false));
633 // clear this list to save a few bytes of RAM
634 pimpl_->session_->lastOpened().clear();
638 LyXView * LyX::newLyXView()
643 // determine windows size and position, from lyxrc and/or session
645 unsigned int width = 690;
646 unsigned int height = 510;
647 // default icon size, will be overwritten by stored session value
648 unsigned int iconSizeXY = 0;
649 int maximized = LyXView::NotMaximized;
651 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
652 width = lyxrc.geometry_width;
653 height = lyxrc.geometry_height;
655 // if lyxrc returns (0,0), then use session info
657 string val = session().sessionInfo().load("WindowWidth");
659 width = convert<unsigned int>(val);
660 val = session().sessionInfo().load("WindowHeight");
662 height = convert<unsigned int>(val);
663 val = session().sessionInfo().load("WindowMaximized");
665 maximized = convert<int>(val);
666 val = session().sessionInfo().load("IconSizeXY");
668 iconSizeXY = convert<unsigned int>(val);
671 // if user wants to restore window position
674 if (lyxrc.geometry_xysaved) {
675 string val = session().sessionInfo().load("WindowPosX");
677 posx = convert<int>(val);
678 val = session().sessionInfo().load("WindowPosY");
680 posy = convert<int>(val);
683 if (!geometryArg.empty())
689 // create the main window
690 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
698 The SIGHUP signal does not exist on Windows and does not need to be handled.
700 Windows handles SIGFPE and SIGSEGV signals as expected.
702 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
703 cause a new thread to be spawned. This may well result in unexpected
704 behaviour by the single-threaded LyX.
706 SIGTERM signals will come only from another process actually sending
707 that signal using 'raise' in Windows' POSIX compatability layer. It will
708 not come from the general "terminate process" methods that everyone
709 actually uses (and which can't be trapped). Killing an app 'politely' on
710 Windows involves first sending a WM_CLOSE message, something that is
711 caught already by the Qt frontend.
713 For more information see:
715 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
716 ...signals are mostly useless on Windows for a variety of reasons that are
719 'UNIX Application Migration Guide, Chapter 9'
720 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
722 'How To Terminate an Application "Cleanly" in Win32'
723 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
727 static void error_handler(int err_sig)
729 // Throw away any signals other than the first one received.
730 static sig_atomic_t handling_error = false;
733 handling_error = true;
735 // We have received a signal indicating a fatal error, so
736 // try and save the data ASAP.
737 LyX::cref().emergencyCleanup();
739 // These lyxerr calls may or may not work:
741 // Signals are asynchronous, so the main program may be in a very
742 // fragile state when a signal is processed and thus while a signal
743 // handler function executes.
744 // In general, therefore, we should avoid performing any
745 // I/O operations or calling most library and system functions from
748 // This shouldn't matter here, however, as we've already invoked
753 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
757 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
760 lyxerr << "\nlyx: SIGSEGV signal caught\n"
761 "Sorry, you have found a bug in LyX. "
762 "Please read the bug-reporting instructions "
763 "in Help->Introduction and send us a bug report, "
764 "if necessary. Thanks !\nBye." << endl;
772 // Deinstall the signal handlers
774 signal(SIGHUP, SIG_DFL);
776 signal(SIGINT, SIG_DFL);
777 signal(SIGFPE, SIG_DFL);
778 signal(SIGSEGV, SIG_DFL);
779 signal(SIGTERM, SIG_DFL);
782 if (err_sig == SIGSEGV ||
783 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
785 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
794 void LyX::printError(ErrorItem const & ei)
796 docstring tmp = _("LyX: ") + ei.error + char_type(':')
798 std::cerr << to_utf8(tmp) << std::endl;
802 void LyX::initGuiFont()
804 if (lyxrc.roman_font_name.empty())
805 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
807 if (lyxrc.sans_font_name.empty())
808 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
810 if (lyxrc.typewriter_font_name.empty())
811 lyxrc.typewriter_font_name
812 = pimpl_->application_->typewriterFontName();
819 signal(SIGHUP, error_handler);
821 signal(SIGFPE, error_handler);
822 signal(SIGSEGV, error_handler);
823 signal(SIGINT, error_handler);
824 signal(SIGTERM, error_handler);
825 // SIGPIPE can be safely ignored.
827 lyxrc.tempdir_path = package().temp_dir().absFilename();
828 lyxrc.document_path = package().document_dir().absFilename();
830 if (lyxrc.template_path.empty()) {
831 lyxrc.template_path = addPath(package().system_support().absFilename(),
836 // Read configuration files
839 // This one may have been distributed along with LyX.
840 if (!readRcFile("lyxrc.dist"))
843 // Set the language defined by the distributor.
844 //setGuiLanguage(lyxrc.gui_language);
846 // Set the PATH correctly.
847 #if !defined (USE_POSIX_PACKAGING)
848 // Add the directory containing the LyX executable to the path
849 // so that LyX can find things like tex2lyx.
850 if (package().build_support().empty())
851 prependEnvPath("PATH", package().binary_dir().absFilename());
853 if (!lyxrc.path_prefix.empty())
854 prependEnvPath("PATH", lyxrc.path_prefix);
856 // Check that user LyX directory is ok.
857 if (queryUserLyXDir(package().explicit_user_support()))
858 reconfigureUserLyXDir();
860 // no need for a splash when there is no GUI
865 // This one is generated in user_support directory by lib/configure.py.
866 if (!readRcFile("lyxrc.defaults"))
869 // Query the OS to know what formats are viewed natively
870 formats.setAutoOpen();
872 // Read lyxrc.dist again to be able to override viewer auto-detection.
873 readRcFile("lyxrc.dist");
875 system_lyxrc = lyxrc;
876 system_formats = formats;
877 pimpl_->system_converters_ = pimpl_->converters_;
878 pimpl_->system_movers_ = pimpl_->movers_;
879 system_lcolor = lcolor;
881 // This one is edited through the preferences dialog.
882 if (!readRcFile("preferences"))
885 if (!readEncodingsFile("encodings", "unicodesymbols"))
887 if (!readLanguagesFile("languages"))
891 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
896 // Set the language defined by the user.
897 //setGuiLanguage(lyxrc.gui_language);
900 pimpl_->toplevel_keymap_.reset(new KeyMap);
901 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
902 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
904 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
907 if (!readUIFile(lyxrc.ui_file))
911 if (lyxerr.debugging(Debug::LYXRC))
914 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
915 if (!lyxrc.path_prefix.empty())
916 prependEnvPath("PATH", lyxrc.path_prefix);
918 FileName const document_path(lyxrc.document_path);
919 if (fs::exists(document_path.toFilesystemEncoding()) &&
920 fs::is_directory(document_path.toFilesystemEncoding()))
921 package().document_dir() = document_path;
923 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
924 if (package().temp_dir().empty()) {
925 Alert::error(_("Could not create temporary directory"),
926 bformat(_("Could not create a temporary directory in\n"
927 "%1$s. Make sure that this\n"
928 "path exists and is writable and try again."),
929 from_utf8(lyxrc.tempdir_path)));
930 // createLyXTmpDir() tries sufficiently hard to create a
931 // usable temp dir, so the probability to come here is
932 // close to zero. We therefore don't try to overcome this
933 // problem with e.g. asking the user for a new path and
934 // trying again but simply exit.
938 LYXERR(Debug::INIT) << "LyX tmp dir: `"
939 << package().temp_dir().absFilename()
942 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
943 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
945 // This must happen after package initialization and after lyxrc is
946 // read, therefore it can't be done by a static object.
947 ConverterCache::init();
953 void LyX::defaultKeyBindings(KeyMap * kbmap)
955 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
956 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
957 kbmap->bind("Up", FuncRequest(LFUN_UP));
958 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
960 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
961 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
962 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
963 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
965 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
966 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
967 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
968 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
970 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
971 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
973 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
974 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
976 // kbmap->bindings to enable the use of the numeric keypad
978 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
979 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
980 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
981 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
982 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
983 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
984 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
985 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
986 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
987 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
988 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
989 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
990 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
991 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
992 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
993 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
994 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
995 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
996 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
997 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
998 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
999 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1000 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1001 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1005 void LyX::emergencyCleanup() const
1007 // what to do about tmpfiles is non-obvious. we would
1008 // like to delete any we find, but our lyxdir might
1009 // contain documents etc. which might be helpful on
1012 pimpl_->buffer_list_.emergencyWriteAll();
1014 if (pimpl_->lyx_server_)
1015 pimpl_->lyx_server_->emergencyCleanup();
1016 pimpl_->lyx_server_.reset();
1017 pimpl_->lyx_socket_.reset();
1022 void LyX::deadKeyBindings(KeyMap * kbmap)
1024 // bindKeyings for transparent handling of deadkeys
1025 // The keysyms are gotten from XFree86 X11R6
1026 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1027 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1028 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1029 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1030 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1031 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1032 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1033 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1034 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1035 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1036 // nothing with this name
1037 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1038 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1039 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1040 // nothing with this name either...
1041 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1042 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1043 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1044 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1050 // return true if file does not exist or is older than configure.py.
1051 bool needsUpdate(string const & file)
1053 // We cannot initialize configure_script directly because the package
1054 // is not initialized yet when static objects are constructed.
1055 static string configure_script;
1056 static bool firstrun = true;
1058 configure_script = FileName(addName(
1059 package().system_support().absFilename(),
1060 "configure.py")).toFilesystemEncoding();
1064 string const absfile = FileName(addName(
1065 package().user_support().absFilename(), file)).toFilesystemEncoding();
1066 return (! fs::exists(absfile))
1067 || (fs::last_write_time(configure_script)
1068 > fs::last_write_time(absfile));
1074 bool LyX::queryUserLyXDir(bool explicit_userdir)
1076 // Does user directory exist?
1077 string const user_support =
1078 package().user_support().toFilesystemEncoding();
1079 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1080 first_start = false;
1082 return needsUpdate("lyxrc.defaults")
1083 || needsUpdate("textclass.lst")
1084 || needsUpdate("packages.lst");
1087 first_start = !explicit_userdir;
1089 // If the user specified explicitly a directory, ask whether
1090 // to create it. If the user says "no", then exit.
1091 if (explicit_userdir &&
1093 _("Missing user LyX directory"),
1094 bformat(_("You have specified a non-existent user "
1095 "LyX directory, %1$s.\n"
1096 "It is needed to keep your own configuration."),
1097 from_utf8(package().user_support().absFilename())),
1099 _("&Create directory"),
1101 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1102 earlyExit(EXIT_FAILURE);
1105 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1106 from_utf8(package().user_support().absFilename())))
1109 if (!createDirectory(package().user_support(), 0755)) {
1110 // Failed, so let's exit.
1111 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1113 earlyExit(EXIT_FAILURE);
1120 bool LyX::readRcFile(string const & name)
1122 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1124 FileName const lyxrc_path = libFileSearch(string(), name);
1125 if (!lyxrc_path.empty()) {
1127 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1129 if (lyxrc.read(lyxrc_path) < 0) {
1130 showFileError(name);
1134 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1140 // Read the ui file `name'
1141 bool LyX::readUIFile(string const & name, bool include)
1151 struct keyword_item uitags[ui_last - 1] = {
1152 { "include", ui_include },
1153 { "menuset", ui_menuset },
1154 { "toolbars", ui_toolbars },
1155 { "toolbarset", ui_toolbarset }
1158 // Ensure that a file is read only once (prevents include loops)
1159 static std::list<string> uifiles;
1160 std::list<string>::const_iterator it = uifiles.begin();
1161 std::list<string>::const_iterator end = uifiles.end();
1162 it = std::find(it, end, name);
1164 LYXERR(Debug::INIT) << "UI file '" << name
1165 << "' has been read already. "
1166 << "Is this an include loop?"
1171 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1176 ui_path = libFileSearch("ui", name, "inc");
1177 if (ui_path.empty())
1178 ui_path = libFileSearch("ui",
1179 changeExtension(name, "inc"));
1182 ui_path = libFileSearch("ui", name, "ui");
1184 if (ui_path.empty()) {
1185 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1186 showFileError(name);
1190 uifiles.push_back(name);
1192 LYXERR(Debug::INIT) << "Found " << name
1193 << " in " << ui_path << endl;
1194 Lexer lex(uitags, ui_last - 1);
1195 lex.setFile(ui_path);
1197 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1201 if (lyxerr.debugging(Debug::PARSER))
1202 lex.printTable(lyxerr);
1204 while (lex.isOK()) {
1205 switch (lex.lex()) {
1208 string const file = lex.getString();
1209 if (!readUIFile(file, true))
1214 menubackend.read(lex);
1218 toolbarbackend.readToolbars(lex);
1222 toolbarbackend.readToolbarSettings(lex);
1226 if (!rtrim(lex.getString()).empty())
1227 lex.printError("LyX::ReadUIFile: "
1228 "Unknown menu tag: `$$Token'");
1236 // Read the languages file `name'
1237 bool LyX::readLanguagesFile(string const & name)
1239 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1241 FileName const lang_path = libFileSearch(string(), name);
1242 if (lang_path.empty()) {
1243 showFileError(name);
1246 languages.read(lang_path);
1251 // Read the encodings file `name'
1252 bool LyX::readEncodingsFile(string const & enc_name,
1253 string const & symbols_name)
1255 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1256 << symbols_name << "..." << endl;
1258 FileName const symbols_path = libFileSearch(string(), symbols_name);
1259 if (symbols_path.empty()) {
1260 showFileError(symbols_name);
1264 FileName const enc_path = libFileSearch(string(), enc_name);
1265 if (enc_path.empty()) {
1266 showFileError(enc_name);
1269 encodings.read(enc_path, symbols_path);
1278 /// return the the number of arguments consumed
1279 typedef boost::function<int(string const &, string const &)> cmd_helper;
1281 int parse_dbg(string const & arg, string const &)
1284 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1285 Debug::showTags(lyxerr);
1288 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1290 lyxerr.level(Debug::value(arg));
1291 Debug::showLevel(lyxerr, lyxerr.level());
1296 int parse_help(string const &, string const &)
1299 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1300 "Command line switches (case sensitive):\n"
1301 "\t-help summarize LyX usage\n"
1302 "\t-userdir dir set user directory to dir\n"
1303 "\t-sysdir dir set system directory to dir\n"
1304 "\t-geometry WxH+X+Y set geometry of the main window\n"
1305 "\t-dbg feature[,feature]...\n"
1306 " select the features to debug.\n"
1307 " Type `lyx -dbg' to see the list of features\n"
1308 "\t-x [--execute] command\n"
1309 " where command is a lyx command.\n"
1310 "\t-e [--export] fmt\n"
1311 " where fmt is the export format of choice.\n"
1312 "\t-i [--import] fmt file.xxx\n"
1313 " where fmt is the import format of choice\n"
1314 " and file.xxx is the file to be imported.\n"
1315 "\t-version summarize version and build info\n"
1316 "Check the LyX man page for more details.")) << endl;
1321 int parse_version(string const &, string const &)
1323 lyxerr << "LyX " << lyx_version
1324 << " (" << lyx_release_date << ")" << endl;
1325 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1327 lyxerr << lyx_version_info << endl;
1332 int parse_sysdir(string const & arg, string const &)
1335 Alert::error(_("No system directory"),
1336 _("Missing directory for -sysdir switch"));
1339 cl_system_support = arg;
1343 int parse_userdir(string const & arg, string const &)
1346 Alert::error(_("No user directory"),
1347 _("Missing directory for -userdir switch"));
1350 cl_user_support = arg;
1354 int parse_execute(string const & arg, string const &)
1357 Alert::error(_("Incomplete command"),
1358 _("Missing command string after --execute switch"));
1365 int parse_export(string const & type, string const &)
1368 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1369 "--export switch")) << endl;
1372 batch = "buffer-export " + type;
1377 int parse_import(string const & type, string const & file)
1380 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1381 "--import switch")) << endl;
1385 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1389 batch = "buffer-import " + type + ' ' + file;
1393 int parse_geometry(string const & arg1, string const &)
1396 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1397 // remove also the arg
1400 // don't remove "-geometry"
1409 void LyX::easyParse(int & argc, char * argv[])
1411 std::map<string, cmd_helper> cmdmap;
1413 cmdmap["-dbg"] = parse_dbg;
1414 cmdmap["-help"] = parse_help;
1415 cmdmap["--help"] = parse_help;
1416 cmdmap["-version"] = parse_version;
1417 cmdmap["--version"] = parse_version;
1418 cmdmap["-sysdir"] = parse_sysdir;
1419 cmdmap["-userdir"] = parse_userdir;
1420 cmdmap["-x"] = parse_execute;
1421 cmdmap["--execute"] = parse_execute;
1422 cmdmap["-e"] = parse_export;
1423 cmdmap["--export"] = parse_export;
1424 cmdmap["-i"] = parse_import;
1425 cmdmap["--import"] = parse_import;
1426 cmdmap["-geometry"] = parse_geometry;
1428 for (int i = 1; i < argc; ++i) {
1429 std::map<string, cmd_helper>::const_iterator it
1430 = cmdmap.find(argv[i]);
1432 // don't complain if not found - may be parsed later
1433 if (it == cmdmap.end())
1436 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1437 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1439 int const remove = 1 + it->second(arg, arg2);
1441 // Now, remove used arguments by shifting
1442 // the following ones remove places down.
1445 for (int j = i; j < argc; ++j)
1446 argv[j] = argv[j + remove];
1451 batch_command = batch;
1455 FuncStatus getStatus(FuncRequest const & action)
1457 return LyX::ref().lyxFunc().getStatus(action);
1461 void dispatch(FuncRequest const & action)
1463 LyX::ref().lyxFunc().dispatch(action);
1467 BufferList & theBufferList()
1469 return LyX::ref().bufferList();
1473 LyXFunc & theLyXFunc()
1475 return LyX::ref().lyxFunc();
1479 Server & theServer()
1481 // FIXME: this should not be use_gui dependent
1482 BOOST_ASSERT(use_gui);
1483 return LyX::ref().server();
1487 ServerSocket & theServerSocket()
1489 // FIXME: this should not be use_gui dependent
1490 BOOST_ASSERT(use_gui);
1491 return LyX::ref().socket();
1495 KeyMap & theTopLevelKeymap()
1497 BOOST_ASSERT(use_gui);
1498 return LyX::ref().topLevelKeymap();
1502 Converters & theConverters()
1504 return LyX::ref().converters();
1508 Converters & theSystemConverters()
1510 return LyX::ref().systemConverters();
1514 Movers & theMovers()
1516 return LyX::ref().pimpl_->movers_;
1520 Mover const & getMover(std::string const & fmt)
1522 return LyX::ref().pimpl_->movers_(fmt);
1526 void setMover(std::string const & fmt, std::string const & command)
1528 LyX::ref().pimpl_->movers_.set(fmt, command);
1532 Movers & theSystemMovers()
1534 return LyX::ref().pimpl_->system_movers_;
1538 Messages & getMessages(std::string const & language)
1540 return LyX::ref().getMessages(language);
1544 Messages & getGuiMessages()
1546 return LyX::ref().getGuiMessages();