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/Dialogs.h"
50 #include "frontends/Gui.h"
51 #include "frontends/LyXView.h"
53 #include "support/environment.h"
54 #include "support/filetools.h"
55 #include "support/lyxlib.h"
56 #include "support/convert.h"
57 #include "support/ExceptionMessage.h"
58 #include "support/os.h"
59 #include "support/Package.h"
60 #include "support/Path.h"
61 #include "support/Systemcall.h"
63 #include <boost/bind.hpp>
64 #include <boost/filesystem/operations.hpp>
80 #ifndef CXX_GLOBAL_CSTD
86 namespace fs = boost::filesystem;
90 using support::addName;
91 using support::addPath;
92 using support::bformat;
93 using support::changeExtension;
94 using support::createDirectory;
95 using support::createLyXTmpDir;
96 using support::destroyDir;
97 using support::FileName;
98 using support::fileSearch;
99 using support::getEnv;
100 using support::i18nLibFileSearch;
101 using support::libFileSearch;
102 using support::package;
103 using support::prependEnvPath;
104 using support::rtrim;
105 using support::Systemcall;
106 using frontend::LyXView;
108 namespace Alert = frontend::Alert;
109 namespace os = support::os;
113 /// are we using the GUI at all?
115 * We default to true and this is changed to false when the export feature is used.
122 // Filled with the command line arguments "foo" of "-sysdir foo" or
124 string cl_system_support;
125 string cl_user_support;
127 std::string geometryArg;
129 LyX * singleton_ = 0;
131 void showFileError(string const & error)
133 Alert::warning(_("Could not read configuration file"),
134 bformat(_("Error while reading the configuration file\n%1$s.\n"
135 "Please check your installation."), from_utf8(error)));
139 void reconfigureUserLyXDir()
141 string const configure_command = package().configure_command();
143 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
144 support::Path p(package().user_support());
146 one.startscript(Systemcall::Wait, configure_command);
147 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
153 /// The main application class private implementation.
154 struct LyX::Singletons
158 // Set the default User Interface language as soon as possible.
159 // The language used will be derived from the environment
161 messages_["GUI"] = Messages();
163 /// our function handler
166 BufferList buffer_list_;
168 boost::scoped_ptr<KeyMap> toplevel_keymap_;
170 boost::scoped_ptr<Server> lyx_server_;
172 boost::scoped_ptr<ServerSocket> lyx_socket_;
174 boost::scoped_ptr<frontend::Application> application_;
175 /// lyx session, containing lastfiles, lastfilepos, and lastopened
176 boost::scoped_ptr<Session> session_;
178 /// Files to load at start.
179 vector<FileName> files_to_load_;
181 /// The messages translators.
182 map<string, Messages> messages_;
184 /// The file converters.
185 Converters converters_;
187 // The system converters copy after reading lyxrc.defaults.
188 Converters system_converters_;
194 Movers system_movers_;
198 frontend::Application * theApp()
201 return singleton_->pimpl_->application_.get();
214 BOOST_ASSERT(singleton_);
219 LyX const & LyX::cref()
221 BOOST_ASSERT(singleton_);
230 pimpl_.reset(new Singletons);
234 BufferList & LyX::bufferList()
236 return pimpl_->buffer_list_;
240 BufferList const & LyX::bufferList() const
242 return pimpl_->buffer_list_;
246 Session & LyX::session()
248 BOOST_ASSERT(pimpl_->session_.get());
249 return *pimpl_->session_.get();
253 Session const & LyX::session() const
255 BOOST_ASSERT(pimpl_->session_.get());
256 return *pimpl_->session_.get();
260 LyXFunc & LyX::lyxFunc()
262 return pimpl_->lyxfunc_;
266 LyXFunc const & LyX::lyxFunc() const
268 return pimpl_->lyxfunc_;
272 Server & LyX::server()
274 BOOST_ASSERT(pimpl_->lyx_server_.get());
275 return *pimpl_->lyx_server_.get();
279 Server const & LyX::server() const
281 BOOST_ASSERT(pimpl_->lyx_server_.get());
282 return *pimpl_->lyx_server_.get();
286 ServerSocket & LyX::socket()
288 BOOST_ASSERT(pimpl_->lyx_socket_.get());
289 return *pimpl_->lyx_socket_.get();
293 ServerSocket const & LyX::socket() const
295 BOOST_ASSERT(pimpl_->lyx_socket_.get());
296 return *pimpl_->lyx_socket_.get();
300 frontend::Application & LyX::application()
302 BOOST_ASSERT(pimpl_->application_.get());
303 return *pimpl_->application_.get();
307 frontend::Application const & LyX::application() const
309 BOOST_ASSERT(pimpl_->application_.get());
310 return *pimpl_->application_.get();
314 KeyMap & LyX::topLevelKeymap()
316 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
317 return *pimpl_->toplevel_keymap_.get();
321 Converters & LyX::converters()
323 return pimpl_->converters_;
327 Converters & LyX::systemConverters()
329 return pimpl_->system_converters_;
333 KeyMap const & LyX::topLevelKeymap() const
335 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
336 return *pimpl_->toplevel_keymap_.get();
340 Messages & LyX::getMessages(std::string const & language)
342 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
344 if (it != pimpl_->messages_.end())
347 std::pair<map<string, Messages>::iterator, bool> result =
348 pimpl_->messages_.insert(std::make_pair(language, Messages(language)));
350 BOOST_ASSERT(result.second);
351 return result.first->second;
355 Messages & LyX::getGuiMessages()
357 return pimpl_->messages_["GUI"];
361 void LyX::setGuiLanguage(std::string const & language)
363 pimpl_->messages_["GUI"] = Messages(language);
367 Buffer const * const LyX::updateInset(Inset const * inset) const
369 if (quitting || !inset)
372 Buffer const * buffer_ptr = 0;
373 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
374 vector<int>::const_iterator it = view_ids.begin();
375 vector<int>::const_iterator const end = view_ids.end();
376 for (; it != end; ++it) {
378 pimpl_->application_->gui().view(*it).updateInset(inset);
386 void LyX::hideDialogs(std::string const & name, Inset * inset) const
388 if (quitting || !use_gui)
391 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
392 vector<int>::const_iterator it = view_ids.begin();
393 vector<int>::const_iterator const end = view_ids.end();
394 for (; it != end; ++it)
395 pimpl_->application_->gui().view(*it).getDialogs().
400 int LyX::exec(int & argc, char * argv[])
402 // Here we need to parse the command line. At least
403 // we need to parse for "-dbg" and "-help"
404 easyParse(argc, argv);
406 try { support::init_package(to_utf8(from_local8bit(argv[0])),
407 cl_system_support, cl_user_support,
408 support::top_build_dir_is_one_level_up);
409 } catch (support::ExceptionMessage const & message) {
410 if (message.type_ == support::ErrorException) {
411 Alert::error(message.title_, message.details_);
413 } else if (message.type_ == support::WarningException) {
414 Alert::warning(message.title_, message.details_);
418 // Reinit the messages machinery in case package() knows
419 // something interesting about the locale directory.
423 // FIXME: create a ConsoleApplication
424 int exit_status = init(argc, argv);
432 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
437 BufferList::iterator begin = pimpl_->buffer_list_.begin();
439 bool final_success = false;
440 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
442 if (buf != buf->getMasterBuffer())
444 bool success = false;
445 buf->dispatch(batch_command, &success);
446 final_success |= success;
449 return !final_success;
452 // Force adding of font path _before_ Application is initialized
453 support::os::addFontResources();
455 // Let the frontend parse and remove all arguments that it knows
456 pimpl_->application_.reset(createApplication(argc, argv));
460 // Parse and remove all known arguments in the LyX singleton
461 // Give an error for all remaining ones.
462 int exit_status = init(argc, argv);
464 // Kill the application object before exiting.
465 pimpl_->application_.reset();
472 /* Create a CoreApplication class that will provide the main event loop
473 * and the socket callback registering. With Qt4, only QtCore
474 * library would be needed.
475 * When this is done, a server_mode could be created and the following two
476 * line would be moved out from here.
478 // Note: socket callback must be registered after init(argc, argv)
479 // such that package().temp_dir() is properly initialized.
480 pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
481 pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
482 FileName(package().temp_dir().absFilename() + "/lyxsocket")));
484 // Start the real execution loop.
485 exit_status = pimpl_->application_->exec();
489 // Restore original font resources after Application is destroyed.
490 support::os::restoreFontResources();
496 void LyX::prepareExit()
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;
930 // Set the language defined by the user.
931 //setGuiLanguage(lyxrc.gui_language);
934 pimpl_->toplevel_keymap_.reset(new KeyMap);
935 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
936 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
938 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
941 if (!readUIFile(lyxrc.ui_file))
945 if (lyxerr.debugging(Debug::LYXRC))
948 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
949 if (!lyxrc.path_prefix.empty())
950 prependEnvPath("PATH", lyxrc.path_prefix);
952 FileName const document_path(lyxrc.document_path);
953 if (fs::exists(document_path.toFilesystemEncoding()) &&
954 fs::is_directory(document_path.toFilesystemEncoding()))
955 package().document_dir() = document_path;
957 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
958 if (package().temp_dir().empty()) {
959 Alert::error(_("Could not create temporary directory"),
960 bformat(_("Could not create a temporary directory in\n"
961 "%1$s. Make sure that this\n"
962 "path exists and is writable and try again."),
963 from_utf8(lyxrc.tempdir_path)));
964 // createLyXTmpDir() tries sufficiently hard to create a
965 // usable temp dir, so the probability to come here is
966 // close to zero. We therefore don't try to overcome this
967 // problem with e.g. asking the user for a new path and
968 // trying again but simply exit.
972 LYXERR(Debug::INIT) << "LyX tmp dir: `"
973 << package().temp_dir().absFilename()
976 LYXERR(Debug::INIT) << "Reading session information '.lyx/session'..." << endl;
977 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
979 // This must happen after package initialization and after lyxrc is
980 // read, therefore it can't be done by a static object.
981 ConverterCache::init();
987 void LyX::defaultKeyBindings(KeyMap * kbmap)
989 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
990 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
991 kbmap->bind("Up", FuncRequest(LFUN_UP));
992 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
994 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
995 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
996 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
997 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
999 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
1000 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
1001 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
1002 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
1004 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
1005 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
1007 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
1008 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
1010 // kbmap->bindings to enable the use of the numeric keypad
1011 // e.g. Num Lock set
1012 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
1013 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
1014 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
1015 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
1016 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
1017 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
1018 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
1019 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
1020 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
1021 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
1022 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
1023 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
1024 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
1025 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
1026 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
1027 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
1028 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
1029 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
1030 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
1031 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
1032 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
1033 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
1034 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
1035 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
1039 void LyX::emergencyCleanup() const
1041 // what to do about tmpfiles is non-obvious. we would
1042 // like to delete any we find, but our lyxdir might
1043 // contain documents etc. which might be helpful on
1046 pimpl_->buffer_list_.emergencyWriteAll();
1048 if (pimpl_->lyx_server_)
1049 pimpl_->lyx_server_->emergencyCleanup();
1050 pimpl_->lyx_server_.reset();
1051 pimpl_->lyx_socket_.reset();
1056 void LyX::deadKeyBindings(KeyMap * kbmap)
1058 // bindKeyings for transparent handling of deadkeys
1059 // The keysyms are gotten from XFree86 X11R6
1060 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
1061 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
1062 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
1063 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
1064 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
1065 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
1066 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
1067 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
1068 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
1069 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
1070 // nothing with this name
1071 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
1072 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
1073 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
1074 // nothing with this name either...
1075 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
1076 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
1077 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
1078 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
1084 // return true if file does not exist or is older than configure.py.
1085 bool needsUpdate(string const & file)
1087 // We cannot initialize configure_script directly because the package
1088 // is not initialized yet when static objects are constructed.
1089 static string configure_script;
1090 static bool firstrun = true;
1092 configure_script = FileName(addName(
1093 package().system_support().absFilename(),
1094 "configure.py")).toFilesystemEncoding();
1098 string const absfile = FileName(addName(
1099 package().user_support().absFilename(), file)).toFilesystemEncoding();
1100 return (! fs::exists(absfile))
1101 || (fs::last_write_time(configure_script)
1102 > fs::last_write_time(absfile));
1108 bool LyX::queryUserLyXDir(bool explicit_userdir)
1110 // Does user directory exist?
1111 string const user_support =
1112 package().user_support().toFilesystemEncoding();
1113 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1114 first_start = false;
1116 return needsUpdate("lyxrc.defaults")
1117 || needsUpdate("textclass.lst")
1118 || needsUpdate("packages.lst");
1121 first_start = !explicit_userdir;
1123 // If the user specified explicitly a directory, ask whether
1124 // to create it. If the user says "no", then exit.
1125 if (explicit_userdir &&
1127 _("Missing user LyX directory"),
1128 bformat(_("You have specified a non-existent user "
1129 "LyX directory, %1$s.\n"
1130 "It is needed to keep your own configuration."),
1131 from_utf8(package().user_support().absFilename())),
1133 _("&Create directory"),
1135 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1136 earlyExit(EXIT_FAILURE);
1139 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1140 from_utf8(package().user_support().absFilename())))
1143 if (!createDirectory(package().user_support(), 0755)) {
1144 // Failed, so let's exit.
1145 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1147 earlyExit(EXIT_FAILURE);
1154 bool LyX::readRcFile(string const & name)
1156 LYXERR(Debug::INIT) << "About to read " << name << "... ";
1158 FileName const lyxrc_path = libFileSearch(string(), name);
1159 if (!lyxrc_path.empty()) {
1161 LYXERR(Debug::INIT) << "Found in " << lyxrc_path << endl;
1163 if (lyxrc.read(lyxrc_path) < 0) {
1164 showFileError(name);
1168 LYXERR(Debug::INIT) << "Not found." << lyxrc_path << endl;
1174 // Read the ui file `name'
1175 bool LyX::readUIFile(string const & name, bool include)
1185 struct keyword_item uitags[ui_last - 1] = {
1186 { "include", ui_include },
1187 { "menuset", ui_menuset },
1188 { "toolbars", ui_toolbars },
1189 { "toolbarset", ui_toolbarset }
1192 // Ensure that a file is read only once (prevents include loops)
1193 static std::list<string> uifiles;
1194 std::list<string>::const_iterator it = uifiles.begin();
1195 std::list<string>::const_iterator end = uifiles.end();
1196 it = std::find(it, end, name);
1198 LYXERR(Debug::INIT) << "UI file '" << name
1199 << "' has been read already. "
1200 << "Is this an include loop?"
1205 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1210 ui_path = libFileSearch("ui", name, "inc");
1211 if (ui_path.empty())
1212 ui_path = libFileSearch("ui",
1213 changeExtension(name, "inc"));
1216 ui_path = libFileSearch("ui", name, "ui");
1218 if (ui_path.empty()) {
1219 LYXERR(Debug::INIT) << "Could not find " << name << endl;
1220 showFileError(name);
1224 uifiles.push_back(name);
1226 LYXERR(Debug::INIT) << "Found " << name
1227 << " in " << ui_path << endl;
1228 Lexer lex(uitags, ui_last - 1);
1229 lex.setFile(ui_path);
1231 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1235 if (lyxerr.debugging(Debug::PARSER))
1236 lex.printTable(lyxerr);
1238 while (lex.isOK()) {
1239 switch (lex.lex()) {
1242 string const file = lex.getString();
1243 if (!readUIFile(file, true))
1248 menubackend.read(lex);
1252 toolbarbackend.readToolbars(lex);
1256 toolbarbackend.readToolbarSettings(lex);
1260 if (!rtrim(lex.getString()).empty())
1261 lex.printError("LyX::ReadUIFile: "
1262 "Unknown menu tag: `$$Token'");
1270 // Read the languages file `name'
1271 bool LyX::readLanguagesFile(string const & name)
1273 LYXERR(Debug::INIT) << "About to read " << name << "..." << endl;
1275 FileName const lang_path = libFileSearch(string(), name);
1276 if (lang_path.empty()) {
1277 showFileError(name);
1280 languages.read(lang_path);
1285 // Read the encodings file `name'
1286 bool LyX::readEncodingsFile(string const & enc_name,
1287 string const & symbols_name)
1289 LYXERR(Debug::INIT) << "About to read " << enc_name << " and "
1290 << symbols_name << "..." << endl;
1292 FileName const symbols_path = libFileSearch(string(), symbols_name);
1293 if (symbols_path.empty()) {
1294 showFileError(symbols_name);
1298 FileName const enc_path = libFileSearch(string(), enc_name);
1299 if (enc_path.empty()) {
1300 showFileError(enc_name);
1303 encodings.read(enc_path, symbols_path);
1312 /// return the the number of arguments consumed
1313 typedef boost::function<int(string const &, string const &)> cmd_helper;
1315 int parse_dbg(string const & arg, string const &)
1318 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1319 Debug::showTags(lyxerr);
1322 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1324 lyxerr.level(Debug::value(arg));
1325 Debug::showLevel(lyxerr, lyxerr.level());
1330 int parse_help(string const &, string const &)
1333 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1334 "Command line switches (case sensitive):\n"
1335 "\t-help summarize LyX usage\n"
1336 "\t-userdir dir set user directory to dir\n"
1337 "\t-sysdir dir set system directory to dir\n"
1338 "\t-geometry WxH+X+Y set geometry of the main window\n"
1339 "\t-dbg feature[,feature]...\n"
1340 " select the features to debug.\n"
1341 " Type `lyx -dbg' to see the list of features\n"
1342 "\t-x [--execute] command\n"
1343 " where command is a lyx command.\n"
1344 "\t-e [--export] fmt\n"
1345 " where fmt is the export format of choice.\n"
1346 "\t-i [--import] fmt file.xxx\n"
1347 " where fmt is the import format of choice\n"
1348 " and file.xxx is the file to be imported.\n"
1349 "\t-version summarize version and build info\n"
1350 "Check the LyX man page for more details.")) << endl;
1355 int parse_version(string const &, string const &)
1357 lyxerr << "LyX " << lyx_version
1358 << " (" << lyx_release_date << ")" << endl;
1359 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1361 lyxerr << lyx_version_info << endl;
1366 int parse_sysdir(string const & arg, string const &)
1369 Alert::error(_("No system directory"),
1370 _("Missing directory for -sysdir switch"));
1373 cl_system_support = arg;
1377 int parse_userdir(string const & arg, string const &)
1380 Alert::error(_("No user directory"),
1381 _("Missing directory for -userdir switch"));
1384 cl_user_support = arg;
1388 int parse_execute(string const & arg, string const &)
1391 Alert::error(_("Incomplete command"),
1392 _("Missing command string after --execute switch"));
1399 int parse_export(string const & type, string const &)
1402 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1403 "--export switch")) << endl;
1406 batch = "buffer-export " + type;
1411 int parse_import(string const & type, string const & file)
1414 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1415 "--import switch")) << endl;
1419 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1423 batch = "buffer-import " + type + ' ' + file;
1427 int parse_geometry(string const & arg1, string const &)
1430 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1431 // remove also the arg
1434 // don't remove "-geometry"
1443 void LyX::easyParse(int & argc, char * argv[])
1445 std::map<string, cmd_helper> cmdmap;
1447 cmdmap["-dbg"] = parse_dbg;
1448 cmdmap["-help"] = parse_help;
1449 cmdmap["--help"] = parse_help;
1450 cmdmap["-version"] = parse_version;
1451 cmdmap["--version"] = parse_version;
1452 cmdmap["-sysdir"] = parse_sysdir;
1453 cmdmap["-userdir"] = parse_userdir;
1454 cmdmap["-x"] = parse_execute;
1455 cmdmap["--execute"] = parse_execute;
1456 cmdmap["-e"] = parse_export;
1457 cmdmap["--export"] = parse_export;
1458 cmdmap["-i"] = parse_import;
1459 cmdmap["--import"] = parse_import;
1460 cmdmap["-geometry"] = parse_geometry;
1462 for (int i = 1; i < argc; ++i) {
1463 std::map<string, cmd_helper>::const_iterator it
1464 = cmdmap.find(argv[i]);
1466 // don't complain if not found - may be parsed later
1467 if (it == cmdmap.end())
1470 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1471 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1473 int const remove = 1 + it->second(arg, arg2);
1475 // Now, remove used arguments by shifting
1476 // the following ones remove places down.
1479 for (int j = i; j < argc; ++j)
1480 argv[j] = argv[j + remove];
1485 batch_command = batch;
1489 FuncStatus getStatus(FuncRequest const & action)
1491 return LyX::ref().lyxFunc().getStatus(action);
1495 void dispatch(FuncRequest const & action)
1497 LyX::ref().lyxFunc().dispatch(action);
1501 BufferList & theBufferList()
1503 return LyX::ref().bufferList();
1507 LyXFunc & theLyXFunc()
1509 return LyX::ref().lyxFunc();
1513 Server & theServer()
1515 // FIXME: this should not be use_gui dependent
1516 BOOST_ASSERT(use_gui);
1517 return LyX::ref().server();
1521 ServerSocket & theServerSocket()
1523 // FIXME: this should not be use_gui dependent
1524 BOOST_ASSERT(use_gui);
1525 return LyX::ref().socket();
1529 KeyMap & theTopLevelKeymap()
1531 BOOST_ASSERT(use_gui);
1532 return LyX::ref().topLevelKeymap();
1536 Converters & theConverters()
1538 return LyX::ref().converters();
1542 Converters & theSystemConverters()
1544 return LyX::ref().systemConverters();
1548 Movers & theMovers()
1550 return LyX::ref().pimpl_->movers_;
1554 Mover const & getMover(std::string const & fmt)
1556 return LyX::ref().pimpl_->movers_(fmt);
1560 void setMover(std::string const & fmt, std::string const & command)
1562 LyX::ref().pimpl_->movers_.set(fmt, command);
1566 Movers & theSystemMovers()
1568 return LyX::ref().pimpl_->system_movers_;
1572 Messages & getMessages(std::string const & language)
1574 return LyX::ref().getMessages(language);
1578 Messages & getGuiMessages()
1580 return LyX::ref().getGuiMessages();