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"
25 #include "CutAndPaste.h"
28 #include "ErrorList.h"
36 #include "LyXAction.h"
40 #include "ModuleList.h"
42 #include "ServerSocket.h"
43 #include "TextClassList.h"
44 #include "MenuBackend.h"
47 #include "ToolbarBackend.h"
49 #include "frontends/alert.h"
50 #include "frontends/Application.h"
51 #include "frontends/Dialogs.h"
52 #include "frontends/Gui.h"
53 #include "frontends/LyXView.h"
55 #include "support/environment.h"
56 #include "support/filetools.h"
57 #include "support/lyxlib.h"
58 #include "support/convert.h"
59 #include "support/ExceptionMessage.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62 #include "support/Path.h"
63 #include "support/Systemcall.h"
65 #include <boost/bind.hpp>
66 #include <boost/filesystem/operations.hpp>
82 #ifndef CXX_GLOBAL_CSTD
88 namespace fs = boost::filesystem;
92 using support::addName;
93 using support::addPath;
94 using support::bformat;
95 using support::changeExtension;
96 using support::createDirectory;
97 using support::createLyXTmpDir;
98 using support::destroyDir;
99 using support::FileName;
100 using support::fileSearch;
101 using support::getEnv;
102 using support::i18nLibFileSearch;
103 using support::libFileSearch;
104 using support::package;
105 using support::prependEnvPath;
106 using support::rtrim;
107 using support::Systemcall;
108 using frontend::LyXView;
110 namespace Alert = frontend::Alert;
111 namespace os = support::os;
115 /// are we using the GUI at all?
117 * We default to true and this is changed to false when the export feature is used.
121 bool quitting; // flag, that we are quitting the program
125 // Filled with the command line arguments "foo" of "-sysdir foo" or
127 string cl_system_support;
128 string cl_user_support;
130 std::string geometryArg;
132 LyX * singleton_ = 0;
134 void showFileError(string const & error)
136 Alert::warning(_("Could not read configuration file"),
137 bformat(_("Error while reading the configuration file\n%1$s.\n"
138 "Please check your installation."), from_utf8(error)));
142 void reconfigureUserLyXDir()
144 string const configure_command = package().configure_command();
146 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
147 support::Path p(package().user_support());
149 one.startscript(Systemcall::Wait, configure_command);
150 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
156 /// The main application class private implementation.
157 struct LyX::Singletons
161 // Set the default User Interface language as soon as possible.
162 // The language used will be derived from the environment
164 messages_["GUI"] = Messages();
166 /// our function handler
169 BufferList buffer_list_;
171 boost::scoped_ptr<KeyMap> toplevel_keymap_;
173 boost::scoped_ptr<Server> lyx_server_;
175 boost::scoped_ptr<ServerSocket> lyx_socket_;
177 boost::scoped_ptr<frontend::Application> application_;
178 /// lyx session, containing lastfiles, lastfilepos, and lastopened
179 boost::scoped_ptr<Session> session_;
181 /// Files to load at start.
182 vector<FileName> files_to_load_;
184 /// The messages translators.
185 map<string, Messages> messages_;
187 /// The file converters.
188 Converters converters_;
190 // The system converters copy after reading lyxrc.defaults.
191 Converters system_converters_;
197 Movers system_movers_;
201 frontend::Application * theApp()
204 return singleton_->pimpl_->application_.get();
217 BOOST_ASSERT(singleton_);
222 LyX const & LyX::cref()
224 BOOST_ASSERT(singleton_);
233 pimpl_.reset(new Singletons);
237 BufferList & LyX::bufferList()
239 return pimpl_->buffer_list_;
243 BufferList const & LyX::bufferList() const
245 return pimpl_->buffer_list_;
249 Session & LyX::session()
251 BOOST_ASSERT(pimpl_->session_.get());
252 return *pimpl_->session_.get();
256 Session const & LyX::session() const
258 BOOST_ASSERT(pimpl_->session_.get());
259 return *pimpl_->session_.get();
263 LyXFunc & LyX::lyxFunc()
265 return pimpl_->lyxfunc_;
269 LyXFunc const & LyX::lyxFunc() const
271 return pimpl_->lyxfunc_;
275 Server & LyX::server()
277 BOOST_ASSERT(pimpl_->lyx_server_.get());
278 return *pimpl_->lyx_server_.get();
282 Server const & LyX::server() const
284 BOOST_ASSERT(pimpl_->lyx_server_.get());
285 return *pimpl_->lyx_server_.get();
289 ServerSocket & LyX::socket()
291 BOOST_ASSERT(pimpl_->lyx_socket_.get());
292 return *pimpl_->lyx_socket_.get();
296 ServerSocket const & LyX::socket() const
298 BOOST_ASSERT(pimpl_->lyx_socket_.get());
299 return *pimpl_->lyx_socket_.get();
303 frontend::Application & LyX::application()
305 BOOST_ASSERT(pimpl_->application_.get());
306 return *pimpl_->application_.get();
310 frontend::Application const & LyX::application() const
312 BOOST_ASSERT(pimpl_->application_.get());
313 return *pimpl_->application_.get();
317 KeyMap & LyX::topLevelKeymap()
319 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
320 return *pimpl_->toplevel_keymap_.get();
324 Converters & LyX::converters()
326 return pimpl_->converters_;
330 Converters & LyX::systemConverters()
332 return pimpl_->system_converters_;
336 KeyMap const & LyX::topLevelKeymap() const
338 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
339 return *pimpl_->toplevel_keymap_.get();
343 Messages & LyX::getMessages(std::string const & language)
345 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
347 if (it != pimpl_->messages_.end())
350 std::pair<map<string, Messages>::iterator, bool> result =
351 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
353 BOOST_ASSERT(result.second);
354 return result.first->second;
358 Messages & LyX::getGuiMessages()
360 return pimpl_->messages_["GUI"];
364 void LyX::setGuiLanguage(std::string const & language)
366 pimpl_->messages_["GUI"] = Messages(language);
370 Buffer const * LyX::updateInset(Inset const * inset) const
372 if (quitting || !inset)
375 Buffer const * buffer_ptr = 0;
376 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
377 vector<int>::const_iterator it = view_ids.begin();
378 vector<int>::const_iterator const end = view_ids.end();
379 for (; it != end; ++it) {
381 pimpl_->application_->gui().view(*it).updateInset(inset);
389 void LyX::hideDialogs(std::string const & name, Inset * inset) const
391 if (quitting || !use_gui)
394 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
395 vector<int>::const_iterator it = view_ids.begin();
396 vector<int>::const_iterator const end = view_ids.end();
397 for (; it != end; ++it)
398 pimpl_->application_->gui().view(*it).getDialogs().
403 int LyX::exec(int & argc, char * argv[])
405 // Here we need to parse the command line. At least
406 // we need to parse for "-dbg" and "-help"
407 easyParse(argc, argv);
410 support::init_package(to_utf8(from_local8bit(argv[0])),
411 cl_system_support, cl_user_support,
412 support::top_build_dir_is_one_level_up);
413 } catch (support::ExceptionMessage const & message) {
414 if (message.type_ == support::ErrorException) {
415 Alert::error(message.title_, message.details_);
417 } else if (message.type_ == support::WarningException) {
418 Alert::warning(message.title_, message.details_);
422 // Reinit the messages machinery in case package() knows
423 // something interesting about the locale directory.
427 // FIXME: create a ConsoleApplication
428 int exit_status = init(argc, argv);
436 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
441 BufferList::iterator begin = pimpl_->buffer_list_.begin();
443 bool final_success = false;
444 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
446 if (buf != buf->getMasterBuffer())
448 bool success = false;
449 buf->dispatch(batch_command, &success);
450 final_success |= success;
453 return !final_success;
456 // Let the frontend parse and remove all arguments that it knows
457 pimpl_->application_.reset(createApplication(argc, argv));
461 // Parse and remove all known arguments in the LyX singleton
462 // Give an error for all remaining ones.
463 int exit_status = init(argc, argv);
465 // Kill the application object before exiting.
466 pimpl_->application_.reset();
473 /* Create a CoreApplication class that will provide the main event loop
474 * and the socket callback registering. With Qt4, only QtCore
475 * library would be needed.
476 * When this is done, a server_mode could be created and the following two
477 * line would be moved out from here.
479 // Note: socket callback must be registered after init(argc, argv)
480 // such that package().temp_dir() is properly initialized.
481 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
482 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
483 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
485 // Start the real execution loop.
486 exit_status = pimpl_->application_->exec();
494 void LyX::prepareExit()
496 // Clear the clipboard and selection stack:
497 cap::clearCutStack();
498 cap::clearSelection();
500 // Set a flag that we do quitting from the program,
501 // so no refreshes are necessary.
504 // close buffers first
505 pimpl_->buffer_list_.closeAll();
507 // do any other cleanup procedures now
508 if (package().temp_dir() != package().system_temp_dir()) {
509 LYXERR(Debug::INFO) << "Deleting tmp dir "
510 << package().temp_dir().absFilename() << endl;
512 if (!destroyDir(package().temp_dir())) {
513 docstring const msg =
514 bformat(_("Unable to remove the temporary directory %1$s"),
515 from_utf8(package().temp_dir().absFilename()));
516 Alert::warning(_("Unable to remove temporary directory"), msg);
521 if (pimpl_->session_)
522 pimpl_->session_->writeFile();
523 pimpl_->session_.reset();
524 pimpl_->lyx_server_.reset();
525 pimpl_->lyx_socket_.reset();
528 // Kill the application object before exiting. This avoids crashes
529 // when exiting on Linux.
530 if (pimpl_->application_)
531 pimpl_->application_.reset();
535 void LyX::earlyExit(int status)
537 BOOST_ASSERT(pimpl_->application_.get());
538 // LyX::pimpl_::application_ is not initialised at this
539 // point so it's safe to just exit after some cleanup.
545 int LyX::init(int & argc, char * argv[])
547 // check for any spurious extra arguments
548 // other than documents
549 for (int argi = 1; argi < argc ; ++argi) {
550 if (argv[argi][0] == '-') {
552 bformat(_("Wrong command line option `%1$s'. Exiting."),
553 from_utf8(argv[argi]))) << endl;
558 // Initialization of LyX (reads lyxrc and more)
559 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
560 bool success = init();
561 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
565 for (int argi = argc - 1; argi >= 1; --argi) {
566 // get absolute path of file and add ".lyx" to
567 // the filename if necessary
568 pimpl_->files_to_load_.push_back(fileSearch(string(),
569 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
570 "lyx", support::allow_unreadable));
574 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
580 void LyX::addFileToLoad(FileName const & fname)
582 vector<FileName>::const_iterator cit = std::find(
583 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
586 if (cit == pimpl_->files_to_load_.end())
587 pimpl_->files_to_load_.push_back(fname);
591 void LyX::loadFiles()
593 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
594 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
596 for (; it != end; ++it) {
600 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
601 if (loadLyXFile(buf, *it)) {
602 ErrorList const & el = buf->errorList("Parse");
604 for_each(el.begin(), el.end(),
605 boost::bind(&LyX::printError, this, _1));
608 pimpl_->buffer_list_.release(buf);
613 void LyX::execBatchCommands()
615 // The advantage of doing this here is that the event loop
616 // is already started. So any need for interaction will be
620 // if reconfiguration is needed.
621 if (textclasslist.empty()) {
622 switch (Alert::prompt(
623 _("No textclass is found"),
624 _("LyX cannot continue because no textclass is found. "
625 "You can either reconfigure normally, or reconfigure using "
626 "default textclasses, or quit LyX."),
633 // regular reconfigure
634 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
637 // reconfigure --without-latex-config
638 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
639 " --without-latex-config"));
642 pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
646 // Execute batch commands if available
647 if (batch_command.empty())
650 LYXERR(Debug::INIT) << "About to handle -x '"
651 << batch_command << '\'' << endl;
653 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
657 void LyX::restoreGuiSession()
659 LyXView * view = newLyXView();
661 // if there is no valid class list, do not load any file.
662 if (textclasslist.empty())
665 // if some files were specified at command-line we assume that the
666 // user wants to edit *these* files and not to restore the session.
667 if (!pimpl_->files_to_load_.empty()) {
668 for_each(pimpl_->files_to_load_.begin(),
669 pimpl_->files_to_load_.end(),
670 bind(&LyXView::loadLyXFile, view, _1, true));
671 // clear this list to save a few bytes of RAM
672 pimpl_->files_to_load_.clear();
673 pimpl_->session_->lastOpened().clear();
675 } else if (lyxrc.load_session) {
676 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
677 // do not add to the lastfile list since these files are restored from
678 // last session, and should be already there (regular files), or should
679 // not be added at all (help files).
680 for_each(lastopened.begin(), lastopened.end(),
681 bind(&LyXView::loadLyXFile, view, _1, false));
683 // clear this list to save a few bytes of RAM
684 pimpl_->session_->lastOpened().clear();
687 BufferList::iterator I = pimpl_->buffer_list_.begin();
688 BufferList::iterator end = pimpl_->buffer_list_.end();
689 for (; I != end; ++I) {
691 if (buf != buf->getMasterBuffer())
696 // FIXME: Switch to the last loaded Buffer. This must not be the first one
697 // because the Buffer won't be connected in this case. The correct solution
698 // would be to avoid the manual connection of the current Buffer in LyXView.
699 if (!pimpl_->buffer_list_.empty())
700 view->setBuffer(pimpl_->buffer_list_.last());
704 LyXView * LyX::newLyXView()
709 // determine windows size and position, from lyxrc and/or session
711 unsigned int width = 690;
712 unsigned int height = 510;
713 // default icon size, will be overwritten by stored session value
714 unsigned int iconSizeXY = 0;
715 int maximized = LyXView::NotMaximized;
717 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
718 width = lyxrc.geometry_width;
719 height = lyxrc.geometry_height;
721 // if lyxrc returns (0,0), then use session info
723 string val = session().sessionInfo().load("WindowWidth");
725 width = convert<unsigned int>(val);
726 val = session().sessionInfo().load("WindowHeight");
728 height = convert<unsigned int>(val);
729 val = session().sessionInfo().load("WindowMaximized");
731 maximized = convert<int>(val);
732 val = session().sessionInfo().load("IconSizeXY");
734 iconSizeXY = convert<unsigned int>(val);
737 // if user wants to restore window position
740 if (lyxrc.geometry_xysaved) {
741 string val = session().sessionInfo().load("WindowPosX");
743 posx = convert<int>(val);
744 val = session().sessionInfo().load("WindowPosY");
746 posy = convert<int>(val);
749 if (!geometryArg.empty())
755 // create the main window
756 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
764 The SIGHUP signal does not exist on Windows and does not need to be handled.
766 Windows handles SIGFPE and SIGSEGV signals as expected.
768 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
769 cause a new thread to be spawned. This may well result in unexpected
770 behaviour by the single-threaded LyX.
772 SIGTERM signals will come only from another process actually sending
773 that signal using 'raise' in Windows' POSIX compatability layer. It will
774 not come from the general "terminate process" methods that everyone
775 actually uses (and which can't be trapped). Killing an app 'politely' on
776 Windows involves first sending a WM_CLOSE message, something that is
777 caught already by the Qt frontend.
779 For more information see:
781 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
782 ...signals are mostly useless on Windows for a variety of reasons that are
785 'UNIX Application Migration Guide, Chapter 9'
786 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
788 'How To Terminate an Application "Cleanly" in Win32'
789 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
793 static void error_handler(int err_sig)
795 // Throw away any signals other than the first one received.
796 static sig_atomic_t handling_error = false;
799 handling_error = true;
801 // We have received a signal indicating a fatal error, so
802 // try and save the data ASAP.
803 LyX::cref().emergencyCleanup();
805 // These lyxerr calls may or may not work:
807 // Signals are asynchronous, so the main program may be in a very
808 // fragile state when a signal is processed and thus while a signal
809 // handler function executes.
810 // In general, therefore, we should avoid performing any
811 // I/O operations or calling most library and system functions from
814 // This shouldn't matter here, however, as we've already invoked
819 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
823 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
826 lyxerr << "\nlyx: SIGSEGV signal caught\n"
827 "Sorry, you have found a bug in LyX. "
828 "Please read the bug-reporting instructions "
829 "in Help->Introduction and send us a bug report, "
830 "if necessary. Thanks !\nBye." << endl;
838 // Deinstall the signal handlers
840 signal(SIGHUP, SIG_DFL);
842 signal(SIGINT, SIG_DFL);
843 signal(SIGFPE, SIG_DFL);
844 signal(SIGSEGV, SIG_DFL);
845 signal(SIGTERM, SIG_DFL);
848 if (err_sig == SIGSEGV ||
849 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
851 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
860 void LyX::printError(ErrorItem const & ei)
862 docstring tmp = _("LyX: ") + ei.error + char_type(':')
864 std::cerr << to_utf8(tmp) << std::endl;
868 void LyX::initGuiFont()
870 if (lyxrc.roman_font_name.empty())
871 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
873 if (lyxrc.sans_font_name.empty())
874 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
876 if (lyxrc.typewriter_font_name.empty())
877 lyxrc.typewriter_font_name
878 = pimpl_->application_->typewriterFontName();
885 signal(SIGHUP, error_handler);
887 signal(SIGFPE, error_handler);
888 signal(SIGSEGV, error_handler);
889 signal(SIGINT, error_handler);
890 signal(SIGTERM, error_handler);
891 // SIGPIPE can be safely ignored.
893 lyxrc.tempdir_path = package().temp_dir().absFilename();
894 lyxrc.document_path = package().document_dir().absFilename();
896 if (lyxrc.template_path.empty()) {
897 lyxrc.template_path = addPath(package().system_support().absFilename(),
902 // Read configuration files
905 // This one may have been distributed along with LyX.
906 if (!readRcFile("lyxrc.dist"))
909 // Set the language defined by the distributor.
910 //setGuiLanguage(lyxrc.gui_language);
912 // Set the PATH correctly.
913 #if !defined (USE_POSIX_PACKAGING)
914 // Add the directory containing the LyX executable to the path
915 // so that LyX can find things like tex2lyx.
916 if (package().build_support().empty())
917 prependEnvPath("PATH", package().binary_dir().absFilename());
919 if (!lyxrc.path_prefix.empty())
920 prependEnvPath("PATH", lyxrc.path_prefix);
922 // Check that user LyX directory is ok.
923 if (queryUserLyXDir(package().explicit_user_support()))
924 reconfigureUserLyXDir();
926 // no need for a splash when there is no GUI
931 // This one is generated in user_support directory by lib/configure.py.
932 if (!readRcFile("lyxrc.defaults"))
935 // Query the OS to know what formats are viewed natively
936 formats.setAutoOpen();
938 // Read lyxrc.dist again to be able to override viewer auto-detection.
939 readRcFile("lyxrc.dist");
941 system_lyxrc = lyxrc;
942 system_formats = formats;
943 pimpl_->system_converters_ = pimpl_->converters_;
944 pimpl_->system_movers_ = pimpl_->movers_;
945 system_lcolor = lcolor;
947 // This one is edited through the preferences dialog.
948 if (!readRcFile("preferences"))
951 if (!readEncodingsFile("encodings", "unicodesymbols"))
953 if (!readLanguagesFile("languages"))
957 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
964 // Set the language defined by the user.
965 //setGuiLanguage(lyxrc.gui_language);
968 pimpl_->toplevel_keymap_.reset(new KeyMap);
969 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
970 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
972 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
975 if (!readUIFile(lyxrc.ui_file))
979 if (lyxerr.debugging(Debug::LYXRC))
982 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
983 if (!lyxrc.path_prefix.empty())
984 prependEnvPath("PATH", lyxrc.path_prefix);
986 FileName const document_path(lyxrc.document_path);
987 if (fs::exists(document_path.toFilesystemEncoding()) &&
988 fs::is_directory(document_path.toFilesystemEncoding()))
989 package().document_dir() = document_path;
991 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
992 if (package().temp_dir().empty()) {
993 Alert::error(_("Could not create temporary directory"),
994 bformat(_("Could not create a temporary directory in\n"
995 "%1$s. Make sure that this\n"
996 "path exists and is writable and try again."),
997 from_utf8(lyxrc.tempdir_path)));
998 // createLyXTmpDir() tries sufficiently hard to create a
999 // usable temp dir, so the probability to come here is
1000 // close to zero. We therefore don't try to overcome this
1001 // problem with e.g. asking the user for a new path and
1002 // trying again but simply exit.
1006 LYXERR(Debug::INIT) << "LyX tmp dir: `"
1007 << package().temp_dir().absFilename()
1010 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
1011 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1013 // This must happen after package initialization and after lyxrc is
1014 // read, therefore it can't be done by a static object.
1015 ConverterCache::init();
1021 void LyX::defaultKeyBindings(KeyMap * kbmap)
1023 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
1024 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
1025 kbmap->bind("Up", FuncRequest(LFUN_UP));
1026 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
1028 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
1029 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
1030 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
1031 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
1033 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1034 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1035 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1036 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1038 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1039 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1041 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1042 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1044 // kbmap->bindings to enable the use of the numeric keypad
1045 // e.g. Num Lock set
1046 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1047 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1048 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1049 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1050 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1051 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1052 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1053 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1054 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1055 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1056 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1057 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1058 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1059 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1060 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1061 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1062 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1063 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1064 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1065 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1066 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1067 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1068 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1069 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1073 void LyX::emergencyCleanup() const
1075 // what to do about tmpfiles is non-obvious. we would
1076 // like to delete any we find, but our lyxdir might
1077 // contain documents etc. which might be helpful on
1080 pimpl_->buffer_list_.emergencyWriteAll();
1082 if (pimpl_->lyx_server_)
1083 pimpl_->lyx_server_->emergencyCleanup();
1084 pimpl_->lyx_server_.reset();
1085 pimpl_->lyx_socket_.reset();
1090 void LyX::deadKeyBindings(KeyMap * kbmap)
1092 // bindKeyings for transparent handling of deadkeys
1093 // The keysyms are gotten from XFree86 X11R6
1094 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1095 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1096 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1097 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1098 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1099 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1100 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1101 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1102 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1103 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1104 // nothing with this name
1105 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1106 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1107 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1108 // nothing with this name either...
1109 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1110 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1111 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1112 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1118 // return true if file does not exist or is older than configure.py.
1119 bool needsUpdate(string const & file)
1121 // We cannot initialize configure_script directly because the package
1122 // is not initialized yet when static objects are constructed.
1123 static string configure_script;
1124 static bool firstrun = true;
1126 configure_script = FileName(addName(
1127 package().system_support().absFilename(),
1128 "configure.py")).toFilesystemEncoding();
1132 string const absfile = FileName(addName(
1133 package().user_support().absFilename(), file)).toFilesystemEncoding();
1134 return (! fs::exists(absfile))
1135 || (fs::last_write_time(configure_script)
1136 > fs::last_write_time(absfile));
1142 bool LyX::queryUserLyXDir(bool explicit_userdir)
1144 // Does user directory exist?
1145 string const user_support =
1146 package().user_support().toFilesystemEncoding();
1147 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1148 first_start = false;
1150 return needsUpdate("lyxrc.defaults")
1151 || needsUpdate("lyxmodules.lst")
1152 || needsUpdate("textclass.lst")
1153 || needsUpdate("packages.lst");
1156 first_start = !explicit_userdir;
1158 // If the user specified explicitly a directory, ask whether
1159 // to create it. If the user says "no", then exit.
1160 if (explicit_userdir &&
1162 _("Missing user LyX directory"),
1163 bformat(_("You have specified a non-existent user "
1164 "LyX directory, %1$s.\n"
1165 "It is needed to keep your own configuration."),
1166 from_utf8(package().user_support().absFilename())),
1168 _("&Create directory"),
1170 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1171 earlyExit(EXIT_FAILURE);
1174 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1175 from_utf8(package().user_support().absFilename())))
1178 if (!createDirectory(package().user_support(), 0755)) {
1179 // Failed, so let's exit.
1180 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1182 earlyExit(EXIT_FAILURE);
1189 bool LyX::readRcFile(string const & name)
1191 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1193 FileName const lyxrc_path = libFileSearch(string(), name);
1194 if (!lyxrc_path.empty()) {
1196 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1198 if (lyxrc.read(lyxrc_path) < 0) {
1199 showFileError(name);
1203 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1209 // Read the ui file `name'
1210 bool LyX::readUIFile(string const & name, bool include)
1220 struct keyword_item uitags[ui_last - 1] = {
1221 { "include", ui_include },
1222 { "menuset", ui_menuset },
1223 { "toolbars", ui_toolbars },
1224 { "toolbarset", ui_toolbarset }
1227 // Ensure that a file is read only once (prevents include loops)
1228 static std::list<string> uifiles;
1229 std::list<string>::const_iterator it = uifiles.begin();
1230 std::list<string>::const_iterator end = uifiles.end();
1231 it = std::find(it, end, name);
1233 LYXERR(Debug::INIT) << "UI file '" << name
1234 << "' has been read already. "
1235 << "Is this an include loop?"
1240 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1245 ui_path = libFileSearch("ui", name, "inc");
1246 if (ui_path.empty())
1247 ui_path = libFileSearch("ui",
1248 changeExtension(name, "inc"));
1251 ui_path = libFileSearch("ui", name, "ui");
1253 if (ui_path.empty()) {
1254 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1255 showFileError(name);
1259 uifiles.push_back(name);
1261 LYXERR(Debug::INIT) << "Found " << name
1262 << " in " << ui_path << endl;
1263 Lexer lex(uitags, ui_last - 1);
1264 lex.setFile(ui_path);
1266 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1270 if (lyxerr.debugging(Debug::PARSER))
1271 lex.printTable(lyxerr);
1273 while (lex.isOK()) {
1274 switch (lex.lex()) {
1277 string const file = lex.getString();
1278 if (!readUIFile(file, true))
1283 menubackend.read(lex);
1287 toolbarbackend.readToolbars(lex);
1291 toolbarbackend.readToolbarSettings(lex);
1295 if (!rtrim(lex.getString()).empty())
1296 lex.printError("LyX::ReadUIFile: "
1297 "Unknown menu tag: `$$Token'");
1305 // Read the languages file `name'
1306 bool LyX::readLanguagesFile(string const & name)
1308 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1310 FileName const lang_path = libFileSearch(string(), name);
1311 if (lang_path.empty()) {
1312 showFileError(name);
1315 languages.read(lang_path);
1320 // Read the encodings file `name'
1321 bool LyX::readEncodingsFile(string const & enc_name,
1322 string const & symbols_name)
1324 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1325 << symbols_name << "..." << endl;
1327 FileName const symbols_path = libFileSearch(string(), symbols_name);
1328 if (symbols_path.empty()) {
1329 showFileError(symbols_name);
1333 FileName const enc_path = libFileSearch(string(), enc_name);
1334 if (enc_path.empty()) {
1335 showFileError(enc_name);
1338 encodings.read(enc_path, symbols_path);
1347 /// return the the number of arguments consumed
1348 typedef boost::function<int(string const &, string const &)> cmd_helper;
1350 int parse_dbg(string const & arg, string const &)
1353 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1354 Debug::showTags(lyxerr);
1357 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1359 lyxerr.level(Debug::value(arg));
1360 Debug::showLevel(lyxerr, lyxerr.level());
1365 int parse_help(string const &, string const &)
1368 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1369 "Command line switches (case sensitive):\n"
1370 "\t-help summarize LyX usage\n"
1371 "\t-userdir dir set user directory to dir\n"
1372 "\t-sysdir dir set system directory to dir\n"
1373 "\t-geometry WxH+X+Y set geometry of the main window\n"
1374 "\t-dbg feature[,feature]...\n"
1375 " select the features to debug.\n"
1376 " Type `lyx -dbg' to see the list of features\n"
1377 "\t-x [--execute] command\n"
1378 " where command is a lyx command.\n"
1379 "\t-e [--export] fmt\n"
1380 " where fmt is the export format of choice.\n"
1381 "\t-i [--import] fmt file.xxx\n"
1382 " where fmt is the import format of choice\n"
1383 " and file.xxx is the file to be imported.\n"
1384 "\t-version summarize version and build info\n"
1385 "Check the LyX man page for more details.")) << endl;
1390 int parse_version(string const &, string const &)
1392 lyxerr << "LyX " << lyx_version
1393 << " (" << lyx_release_date << ")" << endl;
1394 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1396 lyxerr << lyx_version_info << endl;
1401 int parse_sysdir(string const & arg, string const &)
1404 Alert::error(_("No system directory"),
1405 _("Missing directory for -sysdir switch"));
1408 cl_system_support = arg;
1412 int parse_userdir(string const & arg, string const &)
1415 Alert::error(_("No user directory"),
1416 _("Missing directory for -userdir switch"));
1419 cl_user_support = arg;
1423 int parse_execute(string const & arg, string const &)
1426 Alert::error(_("Incomplete command"),
1427 _("Missing command string after --execute switch"));
1434 int parse_export(string const & type, string const &)
1437 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1438 "--export switch")) << endl;
1441 batch = "buffer-export " + type;
1446 int parse_import(string const & type, string const & file)
1449 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1450 "--import switch")) << endl;
1454 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1458 batch = "buffer-import " + type + ' ' + file;
1462 int parse_geometry(string const & arg1, string const &)
1465 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1466 // remove also the arg
1469 // don't remove "-geometry"
1478 void LyX::easyParse(int & argc, char * argv[])
1480 std::map<string, cmd_helper> cmdmap;
1482 cmdmap["-dbg"] = parse_dbg;
1483 cmdmap["-help"] = parse_help;
1484 cmdmap["--help"] = parse_help;
1485 cmdmap["-version"] = parse_version;
1486 cmdmap["--version"] = parse_version;
1487 cmdmap["-sysdir"] = parse_sysdir;
1488 cmdmap["-userdir"] = parse_userdir;
1489 cmdmap["-x"] = parse_execute;
1490 cmdmap["--execute"] = parse_execute;
1491 cmdmap["-e"] = parse_export;
1492 cmdmap["--export"] = parse_export;
1493 cmdmap["-i"] = parse_import;
1494 cmdmap["--import"] = parse_import;
1495 cmdmap["-geometry"] = parse_geometry;
1497 for (int i = 1; i < argc; ++i) {
1498 std::map<string, cmd_helper>::const_iterator it
1499 = cmdmap.find(argv[i]);
1501 // don't complain if not found - may be parsed later
1502 if (it == cmdmap.end())
1505 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1506 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1508 int const remove = 1 + it->second(arg, arg2);
1510 // Now, remove used arguments by shifting
1511 // the following ones remove places down.
1514 for (int j = i; j < argc; ++j)
1515 argv[j] = argv[j + remove];
1520 batch_command = batch;
1524 FuncStatus getStatus(FuncRequest const & action)
1526 return LyX::ref().lyxFunc().getStatus(action);
1530 void dispatch(FuncRequest const & action)
1532 LyX::ref().lyxFunc().dispatch(action);
1536 BufferList & theBufferList()
1538 return LyX::ref().bufferList();
1542 LyXFunc & theLyXFunc()
1544 return LyX::ref().lyxFunc();
1548 Server & theServer()
1550 // FIXME: this should not be use_gui dependent
1551 BOOST_ASSERT(use_gui);
1552 return LyX::ref().server();
1556 ServerSocket & theServerSocket()
1558 // FIXME: this should not be use_gui dependent
1559 BOOST_ASSERT(use_gui);
1560 return LyX::ref().socket();
1564 KeyMap & theTopLevelKeymap()
1566 BOOST_ASSERT(use_gui);
1567 return LyX::ref().topLevelKeymap();
1571 Converters & theConverters()
1573 return LyX::ref().converters();
1577 Converters & theSystemConverters()
1579 return LyX::ref().systemConverters();
1583 Movers & theMovers()
1585 return LyX::ref().pimpl_->movers_;
1589 Mover const & getMover(std::string const & fmt)
1591 return LyX::ref().pimpl_->movers_(fmt);
1595 void setMover(std::string const & fmt, std::string const & command)
1597 LyX::ref().pimpl_->movers_.set(fmt, command);
1601 Movers & theSystemMovers()
1603 return LyX::ref().pimpl_->system_movers_;
1607 Messages & getMessages(std::string const & language)
1609 return LyX::ref().getMessages(language);
1613 Messages & getGuiMessages()
1615 return LyX::ref().getGuiMessages();