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.
124 // Filled with the command line arguments "foo" of "-sysdir foo" or
126 string cl_system_support;
127 string cl_user_support;
129 std::string geometryArg;
131 LyX * singleton_ = 0;
133 void showFileError(string const & error)
135 Alert::warning(_("Could not read configuration file"),
136 bformat(_("Error while reading the configuration file\n%1$s.\n"
137 "Please check your installation."), from_utf8(error)));
141 void reconfigureUserLyXDir()
143 string const configure_command = package().configure_command();
145 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
146 support::Path p(package().user_support());
148 one.startscript(Systemcall::Wait, configure_command);
149 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
155 /// The main application class private implementation.
156 struct LyX::Singletons
160 // Set the default User Interface language as soon as possible.
161 // The language used will be derived from the environment
163 messages_["GUI"] = Messages();
165 /// our function handler
168 BufferList buffer_list_;
170 boost::scoped_ptr<KeyMap> toplevel_keymap_;
172 boost::scoped_ptr<Server> lyx_server_;
174 boost::scoped_ptr<ServerSocket> lyx_socket_;
176 boost::scoped_ptr<frontend::Application> application_;
177 /// lyx session, containing lastfiles, lastfilepos, and lastopened
178 boost::scoped_ptr<Session> session_;
180 /// Files to load at start.
181 vector<FileName> files_to_load_;
183 /// The messages translators.
184 map<string, Messages> messages_;
186 /// The file converters.
187 Converters converters_;
189 // The system converters copy after reading lyxrc.defaults.
190 Converters system_converters_;
196 Movers system_movers_;
200 frontend::Application * theApp()
203 return singleton_->pimpl_->application_.get();
216 BOOST_ASSERT(singleton_);
221 LyX const & LyX::cref()
223 BOOST_ASSERT(singleton_);
232 pimpl_.reset(new Singletons);
236 BufferList & LyX::bufferList()
238 return pimpl_->buffer_list_;
242 BufferList const & LyX::bufferList() const
244 return pimpl_->buffer_list_;
248 Session & LyX::session()
250 BOOST_ASSERT(pimpl_->session_.get());
251 return *pimpl_->session_.get();
255 Session const & LyX::session() const
257 BOOST_ASSERT(pimpl_->session_.get());
258 return *pimpl_->session_.get();
262 LyXFunc & LyX::lyxFunc()
264 return pimpl_->lyxfunc_;
268 LyXFunc const & LyX::lyxFunc() const
270 return pimpl_->lyxfunc_;
274 Server & LyX::server()
276 BOOST_ASSERT(pimpl_->lyx_server_.get());
277 return *pimpl_->lyx_server_.get();
281 Server const & LyX::server() const
283 BOOST_ASSERT(pimpl_->lyx_server_.get());
284 return *pimpl_->lyx_server_.get();
288 ServerSocket & LyX::socket()
290 BOOST_ASSERT(pimpl_->lyx_socket_.get());
291 return *pimpl_->lyx_socket_.get();
295 ServerSocket const & LyX::socket() const
297 BOOST_ASSERT(pimpl_->lyx_socket_.get());
298 return *pimpl_->lyx_socket_.get();
302 frontend::Application & LyX::application()
304 BOOST_ASSERT(pimpl_->application_.get());
305 return *pimpl_->application_.get();
309 frontend::Application const & LyX::application() const
311 BOOST_ASSERT(pimpl_->application_.get());
312 return *pimpl_->application_.get();
316 KeyMap & LyX::topLevelKeymap()
318 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
319 return *pimpl_->toplevel_keymap_.get();
323 Converters & LyX::converters()
325 return pimpl_->converters_;
329 Converters & LyX::systemConverters()
331 return pimpl_->system_converters_;
335 KeyMap const & LyX::topLevelKeymap() const
337 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
338 return *pimpl_->toplevel_keymap_.get();
342 Messages & LyX::getMessages(std::string const & language)
344 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
346 if (it != pimpl_->messages_.end())
349 std::pair<map<string, Messages>::iterator, bool> result =
350 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
352 BOOST_ASSERT(result.second);
353 return result.first->second;
357 Messages & LyX::getGuiMessages()
359 return pimpl_->messages_["GUI"];
363 void LyX::setGuiLanguage(std::string const & language)
365 pimpl_->messages_["GUI"] = Messages(language);
369 Buffer const * LyX::updateInset(Inset const * inset) const
371 if (quitting || !inset)
374 Buffer const * buffer_ptr = 0;
375 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
376 vector<int>::const_iterator it = view_ids.begin();
377 vector<int>::const_iterator const end = view_ids.end();
378 for (; it != end; ++it) {
380 pimpl_->application_->gui().view(*it).updateInset(inset);
388 void LyX::hideDialogs(std::string const & name, Inset * inset) const
390 if (quitting || !use_gui)
393 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
394 vector<int>::const_iterator it = view_ids.begin();
395 vector<int>::const_iterator const end = view_ids.end();
396 for (; it != end; ++it)
397 pimpl_->application_->gui().view(*it).getDialogs().
402 int LyX::exec(int & argc, char * argv[])
404 // Here we need to parse the command line. At least
405 // we need to parse for "-dbg" and "-help"
406 easyParse(argc, argv);
408 try { support::init_package(to_utf8(from_local8bit(argv[0])),
409 cl_system_support, cl_user_support,
410 support::top_build_dir_is_one_level_up);
411 } catch (support::ExceptionMessage const & message) {
412 if (message.type_ == support::ErrorException) {
413 Alert::error(message.title_, message.details_);
415 } else if (message.type_ == support::WarningException) {
416 Alert::warning(message.title_, message.details_);
420 // Reinit the messages machinery in case package() knows
421 // something interesting about the locale directory.
425 // FIXME: create a ConsoleApplication
426 int exit_status = init(argc, argv);
434 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
439 BufferList::iterator begin = pimpl_->buffer_list_.begin();
441 bool final_success = false;
442 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
444 if (buf != buf->getMasterBuffer())
446 bool success = false;
447 buf->dispatch(batch_command, &success);
448 final_success |= success;
451 return !final_success;
454 // Let the frontend parse and remove all arguments that it knows
455 pimpl_->application_.reset(createApplication(argc, argv));
459 // Parse and remove all known arguments in the LyX singleton
460 // Give an error for all remaining ones.
461 int exit_status = init(argc, argv);
463 // Kill the application object before exiting.
464 pimpl_->application_.reset();
471 /* Create a CoreApplication class that will provide the main event loop
472 * and the socket callback registering. With Qt4, only QtCore
473 * library would be needed.
474 * When this is done, a server_mode could be created and the following two
475 * line would be moved out from here.
477 // Note: socket callback must be registered after init(argc, argv)
478 // such that package().temp_dir() is properly initialized.
479 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
480 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
481 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
483 // Start the real execution loop.
484 exit_status = pimpl_->application_->exec();
492 void LyX::prepareExit()
494 // Clear the clipboard and selection stack:
495 cap::clearCutStack();
496 cap::clearSelection();
498 // Set a flag that we do quitting from the program,
499 // so no refreshes are necessary.
502 // close buffers first
503 pimpl_->buffer_list_.closeAll();
505 // do any other cleanup procedures now
506 if (package().temp_dir() != package().system_temp_dir()) {
507 LYXERR(Debug::INFO) << "Deleting tmp dir "
508 << package().temp_dir().absFilename() << endl;
510 if (!destroyDir(package().temp_dir())) {
511 docstring const msg =
512 bformat(_("Unable to remove the temporary directory %1$s"),
513 from_utf8(package().temp_dir().absFilename()));
514 Alert::warning(_("Unable to remove temporary directory"), msg);
519 if (pimpl_->session_)
520 pimpl_->session_->writeFile();
521 pimpl_->session_.reset();
522 pimpl_->lyx_server_.reset();
523 pimpl_->lyx_socket_.reset();
526 // Kill the application object before exiting. This avoids crashes
527 // when exiting on Linux.
528 if (pimpl_->application_)
529 pimpl_->application_.reset();
533 void LyX::earlyExit(int status)
535 BOOST_ASSERT(pimpl_->application_.get());
536 // LyX::pimpl_::application_ is not initialised at this
537 // point so it's safe to just exit after some cleanup.
543 int LyX::init(int & argc, char * argv[])
545 // check for any spurious extra arguments
546 // other than documents
547 for (int argi = 1; argi < argc ; ++argi) {
548 if (argv[argi][0] == '-') {
550 bformat(_("Wrong command line option `%1$s'. Exiting."),
551 from_utf8(argv[argi]))) << endl;
556 // Initialization of LyX (reads lyxrc and more)
557 LYXERR(Debug::INIT) << "Initializing LyX::init..." << endl;
558 bool success = init();
559 LYXERR(Debug::INIT) << "Initializing LyX::init...done" << endl;
563 for (int argi = argc - 1; argi >= 1; --argi) {
564 // get absolute path of file and add ".lyx" to
565 // the filename if necessary
566 pimpl_->files_to_load_.push_back(fileSearch(string(),
567 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
568 "lyx", support::allow_unreadable));
572 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
578 void LyX::addFileToLoad(FileName const & fname)
580 vector<FileName>::const_iterator cit = std::find(
581 pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
584 if (cit == pimpl_->files_to_load_.end())
585 pimpl_->files_to_load_.push_back(fname);
589 void LyX::loadFiles()
591 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
592 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
594 for (; it != end; ++it) {
598 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
599 if (loadLyXFile(buf, *it)) {
600 ErrorList const & el = buf->errorList("Parse");
602 for_each(el.begin(), el.end(),
603 boost::bind(&LyX::printError, this, _1));
606 pimpl_->buffer_list_.release(buf);
611 void LyX::execBatchCommands()
613 // The advantage of doing this here is that the event loop
614 // is already started. So any need for interaction will be
618 // Execute batch commands if available
619 if (batch_command.empty())
622 LYXERR(Debug::INIT) << "About to handle -x '"
623 << batch_command << '\'' << endl;
625 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
629 void LyX::restoreGuiSession()
631 LyXView * view = newLyXView();
633 // if some files were specified at command-line we assume that the
634 // user wants to edit *these* files and not to restore the session.
635 if (!pimpl_->files_to_load_.empty()) {
636 for_each(pimpl_->files_to_load_.begin(),
637 pimpl_->files_to_load_.end(),
638 bind(&LyXView::loadLyXFile, view, _1, true));
639 // clear this list to save a few bytes of RAM
640 pimpl_->files_to_load_.clear();
641 pimpl_->session_->lastOpened().clear();
643 } else if (lyxrc.load_session) {
644 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
645 // do not add to the lastfile list since these files are restored from
646 // last session, and should be already there (regular files), or should
647 // not be added at all (help files).
648 for_each(lastopened.begin(), lastopened.end(),
649 bind(&LyXView::loadLyXFile, view, _1, false));
651 // clear this list to save a few bytes of RAM
652 pimpl_->session_->lastOpened().clear();
655 BufferList::iterator I = pimpl_->buffer_list_.begin();
656 BufferList::iterator end = pimpl_->buffer_list_.end();
657 for (; I != end; ++I) {
659 if (buf != buf->getMasterBuffer())
664 // FIXME: Switch to the last loaded Buffer. This must not be the first one
665 // because the Buffer won't be connected in this case. The correct solution
666 // would be to avoid the manual connection of the current Buffer in LyXView.
667 if (!pimpl_->buffer_list_.empty())
668 view->setBuffer(pimpl_->buffer_list_.last());
672 LyXView * LyX::newLyXView()
677 // determine windows size and position, from lyxrc and/or session
679 unsigned int width = 690;
680 unsigned int height = 510;
681 // default icon size, will be overwritten by stored session value
682 unsigned int iconSizeXY = 0;
683 int maximized = LyXView::NotMaximized;
685 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
686 width = lyxrc.geometry_width;
687 height = lyxrc.geometry_height;
689 // if lyxrc returns (0,0), then use session info
691 string val = session().sessionInfo().load("WindowWidth");
693 width = convert<unsigned int>(val);
694 val = session().sessionInfo().load("WindowHeight");
696 height = convert<unsigned int>(val);
697 val = session().sessionInfo().load("WindowMaximized");
699 maximized = convert<int>(val);
700 val = session().sessionInfo().load("IconSizeXY");
702 iconSizeXY = convert<unsigned int>(val);
705 // if user wants to restore window position
708 if (lyxrc.geometry_xysaved) {
709 string val = session().sessionInfo().load("WindowPosX");
711 posx = convert<int>(val);
712 val = session().sessionInfo().load("WindowPosY");
714 posy = convert<int>(val);
717 if (!geometryArg.empty())
723 // create the main window
724 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
732 The SIGHUP signal does not exist on Windows and does not need to be handled.
734 Windows handles SIGFPE and SIGSEGV signals as expected.
736 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
737 cause a new thread to be spawned. This may well result in unexpected
738 behaviour by the single-threaded LyX.
740 SIGTERM signals will come only from another process actually sending
741 that signal using 'raise' in Windows' POSIX compatability layer. It will
742 not come from the general "terminate process" methods that everyone
743 actually uses (and which can't be trapped). Killing an app 'politely' on
744 Windows involves first sending a WM_CLOSE message, something that is
745 caught already by the Qt frontend.
747 For more information see:
749 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
750 ...signals are mostly useless on Windows for a variety of reasons that are
753 'UNIX Application Migration Guide, Chapter 9'
754 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
756 'How To Terminate an Application "Cleanly" in Win32'
757 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
761 static void error_handler(int err_sig)
763 // Throw away any signals other than the first one received.
764 static sig_atomic_t handling_error = false;
767 handling_error = true;
769 // We have received a signal indicating a fatal error, so
770 // try and save the data ASAP.
771 LyX::cref().emergencyCleanup();
773 // These lyxerr calls may or may not work:
775 // Signals are asynchronous, so the main program may be in a very
776 // fragile state when a signal is processed and thus while a signal
777 // handler function executes.
778 // In general, therefore, we should avoid performing any
779 // I/O operations or calling most library and system functions from
782 // This shouldn't matter here, however, as we've already invoked
787 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
791 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
794 lyxerr << "\nlyx: SIGSEGV signal caught\n"
795 "Sorry, you have found a bug in LyX. "
796 "Please read the bug-reporting instructions "
797 "in Help->Introduction and send us a bug report, "
798 "if necessary. Thanks !\nBye." << endl;
806 // Deinstall the signal handlers
808 signal(SIGHUP, SIG_DFL);
810 signal(SIGINT, SIG_DFL);
811 signal(SIGFPE, SIG_DFL);
812 signal(SIGSEGV, SIG_DFL);
813 signal(SIGTERM, SIG_DFL);
816 if (err_sig == SIGSEGV ||
817 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
819 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
828 void LyX::printError(ErrorItem const & ei)
830 docstring tmp = _("LyX: ") + ei.error + char_type(':')
832 std::cerr << to_utf8(tmp) << std::endl;
836 void LyX::initGuiFont()
838 if (lyxrc.roman_font_name.empty())
839 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
841 if (lyxrc.sans_font_name.empty())
842 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
844 if (lyxrc.typewriter_font_name.empty())
845 lyxrc.typewriter_font_name
846 = pimpl_->application_->typewriterFontName();
853 signal(SIGHUP, error_handler);
855 signal(SIGFPE, error_handler);
856 signal(SIGSEGV, error_handler);
857 signal(SIGINT, error_handler);
858 signal(SIGTERM, error_handler);
859 // SIGPIPE can be safely ignored.
861 lyxrc.tempdir_path = package().temp_dir().absFilename();
862 lyxrc.document_path = package().document_dir().absFilename();
864 if (lyxrc.template_path.empty()) {
865 lyxrc.template_path = addPath(package().system_support().absFilename(),
870 // Read configuration files
873 // This one may have been distributed along with LyX.
874 if (!readRcFile("lyxrc.dist"))
877 // Set the language defined by the distributor.
878 //setGuiLanguage(lyxrc.gui_language);
880 // Set the PATH correctly.
881 #if !defined (USE_POSIX_PACKAGING)
882 // Add the directory containing the LyX executable to the path
883 // so that LyX can find things like tex2lyx.
884 if (package().build_support().empty())
885 prependEnvPath("PATH", package().binary_dir().absFilename());
887 if (!lyxrc.path_prefix.empty())
888 prependEnvPath("PATH", lyxrc.path_prefix);
890 // Check that user LyX directory is ok.
891 if (queryUserLyXDir(package().explicit_user_support()))
892 reconfigureUserLyXDir();
894 // no need for a splash when there is no GUI
899 // This one is generated in user_support directory by lib/configure.py.
900 if (!readRcFile("lyxrc.defaults"))
903 // Query the OS to know what formats are viewed natively
904 formats.setAutoOpen();
906 // Read lyxrc.dist again to be able to override viewer auto-detection.
907 readRcFile("lyxrc.dist");
909 system_lyxrc = lyxrc;
910 system_formats = formats;
911 pimpl_->system_converters_ = pimpl_->converters_;
912 pimpl_->system_movers_ = pimpl_->movers_;
913 system_lcolor = lcolor;
915 // This one is edited through the preferences dialog.
916 if (!readRcFile("preferences"))
919 if (!readEncodingsFile("encodings", "unicodesymbols"))
921 if (!readLanguagesFile("languages"))
925 LYXERR(Debug::INIT) << "Reading layouts..." << endl;
932 // Set the language defined by the user.
933 //setGuiLanguage(lyxrc.gui_language);
936 pimpl_->toplevel_keymap_.reset(new KeyMap);
937 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
938 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
940 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
943 if (!readUIFile(lyxrc.ui_file))
947 if (lyxerr.debugging(Debug::LYXRC))
950 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
951 if (!lyxrc.path_prefix.empty())
952 prependEnvPath("PATH", lyxrc.path_prefix);
954 FileName const document_path(lyxrc.document_path);
955 if (fs::exists(document_path.toFilesystemEncoding()) &&
956 fs::is_directory(document_path.toFilesystemEncoding()))
957 package().document_dir() = document_path;
959 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
960 if (package().temp_dir().empty()) {
961 Alert::error(_("Could not create temporary directory"),
962 bformat(_("Could not create a temporary directory in\n"
963 "%1$s. Make sure that this\n"
964 "path exists and is writable and try again."),
965 from_utf8(lyxrc.tempdir_path)));
966 // createLyXTmpDir() tries sufficiently hard to create a
967 // usable temp dir, so the probability to come here is
968 // close to zero. We therefore don't try to overcome this
969 // problem with e.g. asking the user for a new path and
970 // trying again but simply exit.
974 LYXERR(Debug::INIT) << "LyX tmp dir: `"
975 << package().temp_dir().absFilename()
978 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
979 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
981 // This must happen after package initialization and after lyxrc is
982 // read, therefore it can't be done by a static object.
983 ConverterCache::init();
989 void LyX::defaultKeyBindings(KeyMap * kbmap)
991 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
992 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
993 kbmap->bind("Up", FuncRequest(LFUN_UP));
994 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
996 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
997 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
998 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
999 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
1001 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1002 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1003 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1004 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1006 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1007 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1009 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1010 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1012 // kbmap->bindings to enable the use of the numeric keypad
1013 // e.g. Num Lock set
1014 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1015 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1016 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1017 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1018 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1019 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1020 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1021 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1022 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1023 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1024 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1025 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1026 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1027 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1028 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1029 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1030 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1031 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1032 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1033 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1034 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1035 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1036 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1037 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1041 void LyX::emergencyCleanup() const
1043 // what to do about tmpfiles is non-obvious. we would
1044 // like to delete any we find, but our lyxdir might
1045 // contain documents etc. which might be helpful on
1048 pimpl_->buffer_list_.emergencyWriteAll();
1050 if (pimpl_->lyx_server_)
1051 pimpl_->lyx_server_->emergencyCleanup();
1052 pimpl_->lyx_server_.reset();
1053 pimpl_->lyx_socket_.reset();
1058 void LyX::deadKeyBindings(KeyMap * kbmap)
1060 // bindKeyings for transparent handling of deadkeys
1061 // The keysyms are gotten from XFree86 X11R6
1062 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1063 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1064 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1065 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1066 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1067 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1068 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1069 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1070 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1071 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1072 // nothing with this name
1073 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1074 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1075 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1076 // nothing with this name either...
1077 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1078 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1079 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1080 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1086 // return true if file does not exist or is older than configure.py.
1087 bool needsUpdate(string const & file)
1089 // We cannot initialize configure_script directly because the package
1090 // is not initialized yet when static objects are constructed.
1091 static string configure_script;
1092 static bool firstrun = true;
1094 configure_script = FileName(addName(
1095 package().system_support().absFilename(),
1096 "configure.py")).toFilesystemEncoding();
1100 string const absfile = FileName(addName(
1101 package().user_support().absFilename(), file)).toFilesystemEncoding();
1102 return (! fs::exists(absfile))
1103 || (fs::last_write_time(configure_script)
1104 > fs::last_write_time(absfile));
1110 bool LyX::queryUserLyXDir(bool explicit_userdir)
1112 // Does user directory exist?
1113 string const user_support =
1114 package().user_support().toFilesystemEncoding();
1115 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1116 first_start = false;
1118 return needsUpdate("lyxrc.defaults")
1119 || needsUpdate("lyxmodules.lst")
1120 || needsUpdate("textclass.lst")
1121 || needsUpdate("packages.lst");
1124 first_start = !explicit_userdir;
1126 // If the user specified explicitly a directory, ask whether
1127 // to create it. If the user says "no", then exit.
1128 if (explicit_userdir &&
1130 _("Missing user LyX directory"),
1131 bformat(_("You have specified a non-existent user "
1132 "LyX directory, %1$s.\n"
1133 "It is needed to keep your own configuration."),
1134 from_utf8(package().user_support().absFilename())),
1136 _("&Create directory"),
1138 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1139 earlyExit(EXIT_FAILURE);
1142 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1143 from_utf8(package().user_support().absFilename())))
1146 if (!createDirectory(package().user_support(), 0755)) {
1147 // Failed, so let's exit.
1148 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1150 earlyExit(EXIT_FAILURE);
1157 bool LyX::readRcFile(string const & name)
1159 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1161 FileName const lyxrc_path = libFileSearch(string(), name);
1162 if (!lyxrc_path.empty()) {
1164 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1166 if (lyxrc.read(lyxrc_path) < 0) {
1167 showFileError(name);
1171 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1177 // Read the ui file `name'
1178 bool LyX::readUIFile(string const & name, bool include)
1188 struct keyword_item uitags[ui_last - 1] = {
1189 { "include", ui_include },
1190 { "menuset", ui_menuset },
1191 { "toolbars", ui_toolbars },
1192 { "toolbarset", ui_toolbarset }
1195 // Ensure that a file is read only once (prevents include loops)
1196 static std::list<string> uifiles;
1197 std::list<string>::const_iterator it = uifiles.begin();
1198 std::list<string>::const_iterator end = uifiles.end();
1199 it = std::find(it, end, name);
1201 LYXERR(Debug::INIT) << "UI file '" << name
1202 << "' has been read already. "
1203 << "Is this an include loop?"
1208 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1213 ui_path = libFileSearch("ui", name, "inc");
1214 if (ui_path.empty())
1215 ui_path = libFileSearch("ui",
1216 changeExtension(name, "inc"));
1219 ui_path = libFileSearch("ui", name, "ui");
1221 if (ui_path.empty()) {
1222 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1223 showFileError(name);
1227 uifiles.push_back(name);
1229 LYXERR(Debug::INIT) << "Found " << name
1230 << " in " << ui_path << endl;
1231 Lexer lex(uitags, ui_last - 1);
1232 lex.setFile(ui_path);
1234 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1238 if (lyxerr.debugging(Debug::PARSER))
1239 lex.printTable(lyxerr);
1241 while (lex.isOK()) {
1242 switch (lex.lex()) {
1245 string const file = lex.getString();
1246 if (!readUIFile(file, true))
1251 menubackend.read(lex);
1255 toolbarbackend.readToolbars(lex);
1259 toolbarbackend.readToolbarSettings(lex);
1263 if (!rtrim(lex.getString()).empty())
1264 lex.printError("LyX::ReadUIFile: "
1265 "Unknown menu tag: `$$Token'");
1273 // Read the languages file `name'
1274 bool LyX::readLanguagesFile(string const & name)
1276 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1278 FileName const lang_path = libFileSearch(string(), name);
1279 if (lang_path.empty()) {
1280 showFileError(name);
1283 languages.read(lang_path);
1288 // Read the encodings file `name'
1289 bool LyX::readEncodingsFile(string const & enc_name,
1290 string const & symbols_name)
1292 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1293 << symbols_name << "..." << endl;
1295 FileName const symbols_path = libFileSearch(string(), symbols_name);
1296 if (symbols_path.empty()) {
1297 showFileError(symbols_name);
1301 FileName const enc_path = libFileSearch(string(), enc_name);
1302 if (enc_path.empty()) {
1303 showFileError(enc_name);
1306 encodings.read(enc_path, symbols_path);
1315 /// return the the number of arguments consumed
1316 typedef boost::function<int(string const &, string const &)> cmd_helper;
1318 int parse_dbg(string const & arg, string const &)
1321 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1322 Debug::showTags(lyxerr);
1325 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1327 lyxerr.level(Debug::value(arg));
1328 Debug::showLevel(lyxerr, lyxerr.level());
1333 int parse_help(string const &, string const &)
1336 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1337 "Command line switches (case sensitive):\n"
1338 "\t-help summarize LyX usage\n"
1339 "\t-userdir dir set user directory to dir\n"
1340 "\t-sysdir dir set system directory to dir\n"
1341 "\t-geometry WxH+X+Y set geometry of the main window\n"
1342 "\t-dbg feature[,feature]...\n"
1343 " select the features to debug.\n"
1344 " Type `lyx -dbg' to see the list of features\n"
1345 "\t-x [--execute] command\n"
1346 " where command is a lyx command.\n"
1347 "\t-e [--export] fmt\n"
1348 " where fmt is the export format of choice.\n"
1349 "\t-i [--import] fmt file.xxx\n"
1350 " where fmt is the import format of choice\n"
1351 " and file.xxx is the file to be imported.\n"
1352 "\t-version summarize version and build info\n"
1353 "Check the LyX man page for more details.")) << endl;
1358 int parse_version(string const &, string const &)
1360 lyxerr << "LyX " << lyx_version
1361 << " (" << lyx_release_date << ")" << endl;
1362 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1364 lyxerr << lyx_version_info << endl;
1369 int parse_sysdir(string const & arg, string const &)
1372 Alert::error(_("No system directory"),
1373 _("Missing directory for -sysdir switch"));
1376 cl_system_support = arg;
1380 int parse_userdir(string const & arg, string const &)
1383 Alert::error(_("No user directory"),
1384 _("Missing directory for -userdir switch"));
1387 cl_user_support = arg;
1391 int parse_execute(string const & arg, string const &)
1394 Alert::error(_("Incomplete command"),
1395 _("Missing command string after --execute switch"));
1402 int parse_export(string const & type, string const &)
1405 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1406 "--export switch")) << endl;
1409 batch = "buffer-export " + type;
1414 int parse_import(string const & type, string const & file)
1417 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1418 "--import switch")) << endl;
1422 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1426 batch = "buffer-import " + type + ' ' + file;
1430 int parse_geometry(string const & arg1, string const &)
1433 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1434 // remove also the arg
1437 // don't remove "-geometry"
1446 void LyX::easyParse(int & argc, char * argv[])
1448 std::map<string, cmd_helper> cmdmap;
1450 cmdmap["-dbg"] = parse_dbg;
1451 cmdmap["-help"] = parse_help;
1452 cmdmap["--help"] = parse_help;
1453 cmdmap["-version"] = parse_version;
1454 cmdmap["--version"] = parse_version;
1455 cmdmap["-sysdir"] = parse_sysdir;
1456 cmdmap["-userdir"] = parse_userdir;
1457 cmdmap["-x"] = parse_execute;
1458 cmdmap["--execute"] = parse_execute;
1459 cmdmap["-e"] = parse_export;
1460 cmdmap["--export"] = parse_export;
1461 cmdmap["-i"] = parse_import;
1462 cmdmap["--import"] = parse_import;
1463 cmdmap["-geometry"] = parse_geometry;
1465 for (int i = 1; i < argc; ++i) {
1466 std::map<string, cmd_helper>::const_iterator it
1467 = cmdmap.find(argv[i]);
1469 // don't complain if not found - may be parsed later
1470 if (it == cmdmap.end())
1473 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1474 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1476 int const remove = 1 + it->second(arg, arg2);
1478 // Now, remove used arguments by shifting
1479 // the following ones remove places down.
1482 for (int j = i; j < argc; ++j)
1483 argv[j] = argv[j + remove];
1488 batch_command = batch;
1492 FuncStatus getStatus(FuncRequest const & action)
1494 return LyX::ref().lyxFunc().getStatus(action);
1498 void dispatch(FuncRequest const & action)
1500 LyX::ref().lyxFunc().dispatch(action);
1504 BufferList & theBufferList()
1506 return LyX::ref().bufferList();
1510 LyXFunc & theLyXFunc()
1512 return LyX::ref().lyxFunc();
1516 Server & theServer()
1518 // FIXME: this should not be use_gui dependent
1519 BOOST_ASSERT(use_gui);
1520 return LyX::ref().server();
1524 ServerSocket & theServerSocket()
1526 // FIXME: this should not be use_gui dependent
1527 BOOST_ASSERT(use_gui);
1528 return LyX::ref().socket();
1532 KeyMap & theTopLevelKeymap()
1534 BOOST_ASSERT(use_gui);
1535 return LyX::ref().topLevelKeymap();
1539 Converters & theConverters()
1541 return LyX::ref().converters();
1545 Converters & theSystemConverters()
1547 return LyX::ref().systemConverters();
1551 Movers & theMovers()
1553 return LyX::ref().pimpl_->movers_;
1557 Mover const & getMover(std::string const & fmt)
1559 return LyX::ref().pimpl_->movers_(fmt);
1563 void setMover(std::string const & fmt, std::string const & command)
1565 LyX::ref().pimpl_->movers_.set(fmt, command);
1569 Movers & theSystemMovers()
1571 return LyX::ref().pimpl_->system_movers_;
1575 Messages & getMessages(std::string const & language)
1577 return LyX::ref().getMessages(language);
1581 Messages & getGuiMessages()
1583 return LyX::ref().getGuiMessages();