3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
20 #include "ConverterCache.h"
22 #include "buffer_funcs.h"
23 #include "bufferlist.h"
24 #include "converter.h"
27 #include "errorlist.h"
35 #include "LyXAction.h"
39 #include "lyxserver.h"
40 #include "lyxsocket.h"
41 #include "lyxtextclasslist.h"
42 #include "MenuBackend.h"
44 #include "ToolbarBackend.h"
46 #include "frontends/Alert.h"
47 #include "frontends/Application.h"
48 #include "frontends/Gui.h"
49 #include "frontends/LyXView.h"
51 #include "support/environment.h"
52 #include "support/filetools.h"
53 #include "support/fontutils.h"
54 #include "support/lyxlib.h"
55 #include "support/convert.h"
56 #include "support/os.h"
57 #include "support/package.h"
58 #include "support/path.h"
59 #include "support/systemcall.h"
60 #include "support/unicode.h"
62 #include <boost/bind.hpp>
63 #include <boost/filesystem/operations.hpp>
72 using support::addName;
73 using support::addPath;
74 using support::bformat;
75 using support::createDirectory;
76 using support::createLyXTmpDir;
77 using support::destroyDir;
78 using support::FileName;
79 using support::fileSearch;
80 using support::getEnv;
81 using support::i18nLibFileSearch;
82 using support::libFileSearch;
83 using support::package;
84 using support::prependEnvPath;
86 using support::Systemcall;
88 namespace Alert = frontend::Alert;
89 namespace os = support::os;
90 namespace fs = boost::filesystem;
97 #ifndef CXX_GLOBAL_CSTD
104 /// are we using the GUI at all?
106 * We default to true and this is changed to false when the export feature is used.
113 // Filled with the command line arguments "foo" of "-sysdir foo" or
115 string cl_system_support;
116 string cl_user_support;
118 std::string geometryArg;
120 LyX * singleton_ = 0;
122 void showFileError(string const & error)
124 Alert::warning(_("Could not read configuration file"),
125 bformat(_("Error while reading the configuration file\n%1$s.\n"
126 "Please check your installation."), from_utf8(error)));
130 void reconfigureUserLyXDir()
132 string const configure_command = package().configure_command();
134 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
135 support::Path p(package().user_support());
137 one.startscript(Systemcall::Wait, configure_command);
138 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
144 /// The main application class private implementation.
145 struct LyX::Singletons
147 Singletons(): iconv(ucs4_codeset, "UTF-8")
150 /// our function handler
153 BufferList buffer_list_;
155 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
157 boost::scoped_ptr<LyXServer> lyx_server_;
159 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
161 boost::scoped_ptr<frontend::Application> application_;
162 /// lyx session, containing lastfiles, lastfilepos, and lastopened
163 boost::scoped_ptr<Session> session_;
166 IconvProcessor iconv;
168 /// Files to load at start.
169 vector<FileName> files_to_load_;
173 frontend::Application * theApp()
176 return &singleton_->application();
189 BOOST_ASSERT(singleton_);
194 LyX const & LyX::cref()
196 BOOST_ASSERT(singleton_);
205 pimpl_.reset(new Singletons);
210 BufferList & LyX::bufferList()
212 return pimpl_->buffer_list_;
216 BufferList const & LyX::bufferList() const
218 return pimpl_->buffer_list_;
222 Session & LyX::session()
224 BOOST_ASSERT(pimpl_->session_.get());
225 return *pimpl_->session_.get();
229 Session const & LyX::session() const
231 BOOST_ASSERT(pimpl_->session_.get());
232 return *pimpl_->session_.get();
236 LyXFunc & LyX::lyxFunc()
238 return pimpl_->lyxfunc_;
242 LyXFunc const & LyX::lyxFunc() const
244 return pimpl_->lyxfunc_;
248 LyXServer & LyX::server()
250 BOOST_ASSERT(pimpl_->lyx_server_.get());
251 return *pimpl_->lyx_server_.get();
255 LyXServer const & LyX::server() const
257 BOOST_ASSERT(pimpl_->lyx_server_.get());
258 return *pimpl_->lyx_server_.get();
262 LyXServerSocket & LyX::socket()
264 BOOST_ASSERT(pimpl_->lyx_socket_.get());
265 return *pimpl_->lyx_socket_.get();
269 LyXServerSocket const & LyX::socket() const
271 BOOST_ASSERT(pimpl_->lyx_socket_.get());
272 return *pimpl_->lyx_socket_.get();
276 frontend::Application & LyX::application()
278 BOOST_ASSERT(pimpl_->application_.get());
279 return *pimpl_->application_.get();
283 frontend::Application const & LyX::application() const
285 BOOST_ASSERT(pimpl_->application_.get());
286 return *pimpl_->application_.get();
290 kb_keymap & LyX::topLevelKeymap()
292 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
293 return *pimpl_->toplevel_keymap_.get();
297 IconvProcessor & LyX::iconvProcessor()
299 return pimpl_->iconv;
303 kb_keymap const & LyX::topLevelKeymap() const
305 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
306 return *pimpl_->toplevel_keymap_.get();
310 Buffer const * const LyX::updateInset(InsetBase const * inset) const
315 Buffer const * buffer_ptr = 0;
316 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
317 vector<int>::const_iterator it = view_ids.begin();
318 vector<int>::const_iterator const end = view_ids.end();
319 for (; it != end; ++it) {
321 pimpl_->application_->gui().view(*it).updateInset(inset);
329 int LyX::exec(int & argc, char * argv[])
331 // Here we need to parse the command line. At least
332 // we need to parse for "-dbg" and "-help"
333 easyParse(argc, argv);
335 support::init_package(to_utf8(from_local8bit(argv[0])),
336 cl_system_support, cl_user_support,
337 support::top_build_dir_is_one_level_up);
340 // FIXME: create a ConsoleApplication
341 int exit_status = init(argc, argv);
349 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
354 BufferList::iterator begin = pimpl_->buffer_list_.begin();
355 BufferList::iterator end = pimpl_->buffer_list_.end();
357 bool final_success = false;
358 for (BufferList::iterator I = begin; I != end; ++I) {
360 bool success = false;
361 buf->dispatch(batch_command, &success);
362 final_success |= success;
365 return !final_success;
368 // Force adding of font path _before_ Application is initialized
369 support::addFontResources();
371 // Let the frontend parse and remove all arguments that it knows
372 pimpl_->application_.reset(createApplication(argc, argv));
376 // Parse and remove all known arguments in the LyX singleton
377 // Give an error for all remaining ones.
378 int exit_status = init(argc, argv);
380 // Kill the application object before exiting.
381 pimpl_->application_.reset();
388 /* Create a CoreApplication class that will provide the main event loop
389 * and the socket callback registering. With Qt4, only QtCore
390 * library would be needed.
391 * When this is done, a server_mode could be created and the following two
392 * line would be moved out from here.
394 // Note: socket callback must be registered after init(argc, argv)
395 // such that package().temp_dir() is properly initialized.
396 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
397 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
398 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
400 // Start the real execution loop.
401 exit_status = pimpl_->application_->exec();
405 // Restore original font resources after Application is destroyed.
406 support::restoreFontResources();
412 void LyX::prepareExit()
414 // Set a flag that we do quitting from the program,
415 // so no refreshes are necessary.
418 // close buffers first
419 pimpl_->buffer_list_.closeAll();
421 // do any other cleanup procedures now
422 if (package().temp_dir() != package().system_temp_dir()) {
423 lyxerr[Debug::INFO] << "Deleting tmp dir "
424 << package().temp_dir() << endl;
426 if (!destroyDir(FileName(package().temp_dir()))) {
427 docstring const msg =
428 bformat(_("Unable to remove the temporary directory %1$s"),
429 from_utf8(package().temp_dir()));
430 Alert::warning(_("Unable to remove temporary directory"), msg);
435 if (pimpl_->session_)
436 pimpl_->session_->writeFile();
437 pimpl_->session_.reset();
438 pimpl_->lyx_server_.reset();
439 pimpl_->lyx_socket_.reset();
442 // Kill the application object before exiting. This avoids crashes
443 // when exiting on Linux.
444 if (pimpl_->application_)
445 pimpl_->application_.reset();
449 void LyX::earlyExit(int status)
451 BOOST_ASSERT(pimpl_->application_.get());
452 // LyX::pimpl_::application_ is not initialised at this
453 // point so it's safe to just exit after some cleanup.
459 int LyX::init(int & argc, char * argv[])
461 // check for any spurious extra arguments
462 // other than documents
463 for (int argi = 1; argi < argc ; ++argi) {
464 if (argv[argi][0] == '-') {
466 bformat(_("Wrong command line option `%1$s'. Exiting."),
467 from_utf8(argv[argi]))) << endl;
472 // Initialization of LyX (reads lyxrc and more)
473 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
474 bool success = init();
475 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
479 for (int argi = argc - 1; argi >= 1; --argi) {
480 // get absolute path of file and add ".lyx" to
481 // the filename if necessary
482 pimpl_->files_to_load_.push_back(fileSearch(string(),
483 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
484 "lyx", support::allow_unreadable));
488 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
494 void LyX::loadFiles()
496 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
497 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
499 for (; it != end; ++it) {
503 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
504 if (loadLyXFile(buf, *it)) {
505 ErrorList const & el = buf->errorList("Parse");
507 for_each(el.begin(), el.end(),
508 boost::bind(&LyX::printError, this, _1));
511 pimpl_->buffer_list_.release(buf);
516 void LyX::execBatchCommands()
518 // The advantage of doing this here is that the event loop
519 // is already started. So any need for interaction will be
523 // Execute batch commands if available
524 if (batch_command.empty())
527 lyxerr[Debug::INIT] << "About to handle -x '"
528 << batch_command << '\'' << endl;
530 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
534 void LyX::restoreGuiSession()
536 LyXView * view = newLyXView();
538 // if some files were specified at command-line we assume that the
539 // user wants to edit *these* files and not to restore the session.
540 if (!pimpl_->files_to_load_.empty()) {
541 for_each(pimpl_->files_to_load_.begin(),
542 pimpl_->files_to_load_.end(),
543 bind(&LyXView::loadLyXFile, view, _1, true));
544 // clear this list to save a few bytes of RAM
545 pimpl_->files_to_load_.clear();
546 pimpl_->session_->lastOpened().clear();
550 if (!lyxrc.load_session)
553 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
554 // do not add to the lastfile list since these files are restored from
555 // last session, and should be already there (regular files), or should
556 // not be added at all (help files).
557 for_each(lastopened.begin(), lastopened.end(),
558 bind(&LyXView::loadLyXFile, view, _1, false));
560 // clear this list to save a few bytes of RAM
561 pimpl_->session_->lastOpened().clear();
565 LyXView * LyX::newLyXView()
570 // determine windows size and position, from lyxrc and/or session
572 unsigned int width = 690;
573 unsigned int height = 510;
574 // default icon size, will be overwritten by stored session value
575 unsigned int iconSizeXY = 0;
576 bool maximize = false;
578 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
579 width = lyxrc.geometry_width;
580 height = lyxrc.geometry_height;
582 // if lyxrc returns (0,0), then use session info
584 string val = session().sessionInfo().load("WindowWidth");
586 width = convert<unsigned int>(val);
587 val = session().sessionInfo().load("WindowHeight");
589 height = convert<unsigned int>(val);
590 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
592 val = session().sessionInfo().load("IconSizeXY");
594 iconSizeXY = convert<unsigned int>(val);
597 // if user wants to restore window position
600 if (lyxrc.geometry_xysaved) {
601 string val = session().sessionInfo().load("WindowPosX");
603 posx = convert<int>(val);
604 val = session().sessionInfo().load("WindowPosY");
606 posy = convert<int>(val);
609 if (!geometryArg.empty())
615 // create the main window
616 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
624 The SIGHUP signal does not exist on Windows and does not need to be handled.
626 Windows handles SIGFPE and SIGSEGV signals as expected.
628 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
629 cause a new thread to be spawned. This may well result in unexpected
630 behaviour by the single-threaded LyX.
632 SIGTERM signals will come only from another process actually sending
633 that signal using 'raise' in Windows' POSIX compatability layer. It will
634 not come from the general "terminate process" methods that everyone
635 actually uses (and which can't be trapped). Killing an app 'politely' on
636 Windows involves first sending a WM_CLOSE message, something that is
637 caught already by the Qt frontend.
639 For more information see:
641 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
642 ...signals are mostly useless on Windows for a variety of reasons that are
645 'UNIX Application Migration Guide, Chapter 9'
646 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
648 'How To Terminate an Application "Cleanly" in Win32'
649 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
653 static void error_handler(int err_sig)
655 // Throw away any signals other than the first one received.
656 static sig_atomic_t handling_error = false;
659 handling_error = true;
661 // We have received a signal indicating a fatal error, so
662 // try and save the data ASAP.
663 LyX::cref().emergencyCleanup();
665 // These lyxerr calls may or may not work:
667 // Signals are asynchronous, so the main program may be in a very
668 // fragile state when a signal is processed and thus while a signal
669 // handler function executes.
670 // In general, therefore, we should avoid performing any
671 // I/O operations or calling most library and system functions from
674 // This shouldn't matter here, however, as we've already invoked
679 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
683 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
686 lyxerr << "\nlyx: SIGSEGV signal caught\n"
687 "Sorry, you have found a bug in LyX. "
688 "Please read the bug-reporting instructions "
689 "in Help->Introduction and send us a bug report, "
690 "if necessary. Thanks !\nBye." << endl;
698 // Deinstall the signal handlers
700 signal(SIGHUP, SIG_DFL);
702 signal(SIGINT, SIG_DFL);
703 signal(SIGFPE, SIG_DFL);
704 signal(SIGSEGV, SIG_DFL);
705 signal(SIGTERM, SIG_DFL);
708 if (err_sig == SIGSEGV ||
709 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
711 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
720 void LyX::printError(ErrorItem const & ei)
722 docstring tmp = _("LyX: ") + ei.error + char_type(':')
724 std::cerr << to_utf8(tmp) << std::endl;
728 void LyX::initGuiFont()
730 if (lyxrc.roman_font_name.empty())
731 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
733 if (lyxrc.sans_font_name.empty())
734 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
736 if (lyxrc.typewriter_font_name.empty())
737 lyxrc.typewriter_font_name
738 = pimpl_->application_->typewriterFontName();
745 signal(SIGHUP, error_handler);
747 signal(SIGFPE, error_handler);
748 signal(SIGSEGV, error_handler);
749 signal(SIGINT, error_handler);
750 signal(SIGTERM, error_handler);
751 // SIGPIPE can be safely ignored.
753 lyxrc.tempdir_path = package().temp_dir();
754 lyxrc.document_path = package().document_dir();
756 if (lyxrc.template_path.empty()) {
757 lyxrc.template_path = addPath(package().system_support(),
762 // Read configuration files
765 // This one may have been distributed along with LyX.
766 if (!readRcFile("lyxrc.dist"))
769 // Set the PATH correctly.
770 #if !defined (USE_POSIX_PACKAGING)
771 // Add the directory containing the LyX executable to the path
772 // so that LyX can find things like tex2lyx.
773 if (package().build_support().empty())
774 prependEnvPath("PATH", package().binary_dir());
776 if (!lyxrc.path_prefix.empty())
777 prependEnvPath("PATH", lyxrc.path_prefix);
779 // Check that user LyX directory is ok.
780 if (queryUserLyXDir(package().explicit_user_support()))
781 reconfigureUserLyXDir();
783 // no need for a splash when there is no GUI
788 // This one is generated in user_support directory by lib/configure.py.
789 if (!readRcFile("lyxrc.defaults"))
792 // Query the OS to know what formats are viewed natively
793 formats.setAutoOpen();
795 // Read lyxrc.dist again to be able to override viewer auto-detection.
796 readRcFile("lyxrc.dist");
798 system_lyxrc = lyxrc;
799 system_formats = formats;
800 system_converters = converters;
801 system_movers = movers;
802 system_lcolor = lcolor;
804 // This one is edited through the preferences dialog.
805 if (!readRcFile("preferences"))
808 if (!readEncodingsFile("encodings"))
810 if (!readLanguagesFile("languages"))
814 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
820 pimpl_->toplevel_keymap_.reset(new kb_keymap);
821 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
822 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
824 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
827 if (!readUIFile(lyxrc.ui_file))
831 if (lyxerr.debugging(Debug::LYXRC))
834 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
835 if (!lyxrc.path_prefix.empty())
836 prependEnvPath("PATH", lyxrc.path_prefix);
838 FileName const document_path(lyxrc.document_path);
839 if (fs::exists(document_path.toFilesystemEncoding()) &&
840 fs::is_directory(document_path.toFilesystemEncoding()))
841 package().document_dir() = lyxrc.document_path;
843 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
844 if (package().temp_dir().empty()) {
845 Alert::error(_("Could not create temporary directory"),
846 bformat(_("Could not create a temporary directory in\n"
847 "%1$s. Make sure that this\n"
848 "path exists and is writable and try again."),
849 from_utf8(lyxrc.tempdir_path)));
850 // createLyXTmpDir() tries sufficiently hard to create a
851 // usable temp dir, so the probability to come here is
852 // close to zero. We therefore don't try to overcome this
853 // problem with e.g. asking the user for a new path and
854 // trying again but simply exit.
858 if (lyxerr.debugging(Debug::INIT)) {
859 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
862 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
863 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
865 // This must happen after package initialization and after lyxrc is
866 // read, therefore it can't be done by a static object.
867 ConverterCache::init();
873 void LyX::defaultKeyBindings(kb_keymap * kbmap)
875 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
876 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
877 kbmap->bind("Up", FuncRequest(LFUN_UP));
878 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
880 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
881 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
882 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
883 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
885 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
886 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
887 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
888 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
890 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
891 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
893 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
894 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
896 // kbmap->bindings to enable the use of the numeric keypad
898 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
899 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
900 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
901 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
902 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
903 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
904 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
906 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
914 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
915 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
916 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
917 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
918 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
919 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
920 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
921 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
925 void LyX::emergencyCleanup() const
927 // what to do about tmpfiles is non-obvious. we would
928 // like to delete any we find, but our lyxdir might
929 // contain documents etc. which might be helpful on
932 pimpl_->buffer_list_.emergencyWriteAll();
934 if (pimpl_->lyx_server_)
935 pimpl_->lyx_server_->emergencyCleanup();
936 pimpl_->lyx_server_.reset();
937 pimpl_->lyx_socket_.reset();
942 void LyX::deadKeyBindings(kb_keymap * kbmap)
944 // bindKeyings for transparent handling of deadkeys
945 // The keysyms are gotten from XFree86 X11R6
946 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
947 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
948 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
949 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
950 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
951 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
952 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
953 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
954 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
955 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
956 // nothing with this name
957 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
958 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
959 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
960 // nothing with this name either...
961 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
962 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
963 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
964 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
970 // return true if file does not exist or is older than configure.py.
971 bool needsUpdate(string const & file)
973 // We cannot initialize configure_script directly because the package
974 // is not initialized yet when static objects are constructed.
975 static string configure_script;
976 static bool firstrun = true;
978 configure_script = FileName(addName(
979 package().system_support(),
980 "configure.py")).toFilesystemEncoding();
984 string const absfile = FileName(addName(
985 package().user_support(), file)).toFilesystemEncoding();
986 return (! fs::exists(absfile))
987 || (fs::last_write_time(configure_script)
988 > fs::last_write_time(absfile));
994 bool LyX::queryUserLyXDir(bool explicit_userdir)
996 // Does user directory exist?
997 string const user_support =
998 FileName(package().user_support()).toFilesystemEncoding();
999 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1000 first_start = false;
1002 return needsUpdate("lyxrc.defaults")
1003 || needsUpdate("textclass.lst")
1004 || needsUpdate("packages.lst");
1007 first_start = !explicit_userdir;
1009 // If the user specified explicitly a directory, ask whether
1010 // to create it. If the user says "no", then exit.
1011 if (explicit_userdir &&
1013 _("Missing user LyX directory"),
1014 bformat(_("You have specified a non-existent user "
1015 "LyX directory, %1$s.\n"
1016 "It is needed to keep your own configuration."),
1017 from_utf8(package().user_support())),
1019 _("&Create directory"),
1021 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1022 earlyExit(EXIT_FAILURE);
1025 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1026 from_utf8(package().user_support())))
1029 if (!createDirectory(package().user_support(), 0755)) {
1030 // Failed, so let's exit.
1031 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1033 earlyExit(EXIT_FAILURE);
1040 bool LyX::readRcFile(string const & name)
1042 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1044 FileName const lyxrc_path = libFileSearch(string(), name);
1045 if (!lyxrc_path.empty()) {
1047 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1049 if (lyxrc.read(lyxrc_path) < 0) {
1050 showFileError(name);
1054 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1060 // Read the ui file `name'
1061 bool LyX::readUIFile(string const & name)
1071 struct keyword_item uitags[ui_last - 1] = {
1072 { "include", ui_include },
1073 { "menuset", ui_menuset },
1074 { "toolbar", ui_toolbar },
1075 { "toolbars", ui_toolbars }
1078 // Ensure that a file is read only once (prevents include loops)
1079 static std::list<string> uifiles;
1080 std::list<string>::const_iterator it = uifiles.begin();
1081 std::list<string>::const_iterator end = uifiles.end();
1082 it = std::find(it, end, name);
1084 lyxerr[Debug::INIT] << "UI file '" << name
1085 << "' has been read already. "
1086 << "Is this an include loop?"
1091 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1093 FileName const ui_path = libFileSearch("ui", name, "ui");
1095 if (ui_path.empty()) {
1096 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1097 showFileError(name);
1100 uifiles.push_back(name);
1102 lyxerr[Debug::INIT] << "Found " << name
1103 << " in " << ui_path << endl;
1104 LyXLex lex(uitags, ui_last - 1);
1105 lex.setFile(ui_path);
1107 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1111 if (lyxerr.debugging(Debug::PARSER))
1112 lex.printTable(lyxerr);
1114 while (lex.isOK()) {
1115 switch (lex.lex()) {
1118 string const file = lex.getString();
1119 if (!readUIFile(file))
1124 menubackend.read(lex);
1128 toolbarbackend.read(lex);
1132 toolbarbackend.readToolbars(lex);
1136 if (!rtrim(lex.getString()).empty())
1137 lex.printError("LyX::ReadUIFile: "
1138 "Unknown menu tag: `$$Token'");
1146 // Read the languages file `name'
1147 bool LyX::readLanguagesFile(string const & name)
1149 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1151 FileName const lang_path = libFileSearch(string(), name);
1152 if (lang_path.empty()) {
1153 showFileError(name);
1156 languages.read(lang_path);
1161 // Read the encodings file `name'
1162 bool LyX::readEncodingsFile(string const & name)
1164 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1166 FileName const enc_path = libFileSearch(string(), name);
1167 if (enc_path.empty()) {
1168 showFileError(name);
1171 encodings.read(enc_path);
1180 /// return the the number of arguments consumed
1181 typedef boost::function<int(string const &, string const &)> cmd_helper;
1183 int parse_dbg(string const & arg, string const &)
1186 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1187 Debug::showTags(lyxerr);
1190 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1192 lyxerr.level(Debug::value(arg));
1193 Debug::showLevel(lyxerr, lyxerr.level());
1198 int parse_help(string const &, string const &)
1201 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1202 "Command line switches (case sensitive):\n"
1203 "\t-help summarize LyX usage\n"
1204 "\t-userdir dir set user directory to dir\n"
1205 "\t-sysdir dir set system directory to dir\n"
1206 "\t-geometry WxH+X+Y set geometry of the main window\n"
1207 "\t-dbg feature[,feature]...\n"
1208 " select the features to debug.\n"
1209 " Type `lyx -dbg' to see the list of features\n"
1210 "\t-x [--execute] command\n"
1211 " where command is a lyx command.\n"
1212 "\t-e [--export] fmt\n"
1213 " where fmt is the export format of choice.\n"
1214 "\t-i [--import] fmt file.xxx\n"
1215 " where fmt is the import format of choice\n"
1216 " and file.xxx is the file to be imported.\n"
1217 "\t-version summarize version and build info\n"
1218 "Check the LyX man page for more details.")) << endl;
1223 int parse_version(string const &, string const &)
1225 lyxerr << "LyX " << lyx_version
1226 << " (" << lyx_release_date << ")" << endl;
1227 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1229 lyxerr << lyx_version_info << endl;
1234 int parse_sysdir(string const & arg, string const &)
1237 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1240 cl_system_support = arg;
1244 int parse_userdir(string const & arg, string const &)
1247 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1250 cl_user_support = arg;
1254 int parse_execute(string const & arg, string const &)
1257 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1264 int parse_export(string const & type, string const &)
1267 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1268 "--export switch")) << endl;
1271 batch = "buffer-export " + type;
1276 int parse_import(string const & type, string const & file)
1279 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1280 "--import switch")) << endl;
1284 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1288 batch = "buffer-import " + type + ' ' + file;
1292 int parse_geometry(string const & arg1, string const &)
1295 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1296 // remove also the arg
1299 // don't remove "-geometry"
1308 void LyX::easyParse(int & argc, char * argv[])
1310 std::map<string, cmd_helper> cmdmap;
1312 cmdmap["-dbg"] = parse_dbg;
1313 cmdmap["-help"] = parse_help;
1314 cmdmap["--help"] = parse_help;
1315 cmdmap["-version"] = parse_version;
1316 cmdmap["--version"] = parse_version;
1317 cmdmap["-sysdir"] = parse_sysdir;
1318 cmdmap["-userdir"] = parse_userdir;
1319 cmdmap["-x"] = parse_execute;
1320 cmdmap["--execute"] = parse_execute;
1321 cmdmap["-e"] = parse_export;
1322 cmdmap["--export"] = parse_export;
1323 cmdmap["-i"] = parse_import;
1324 cmdmap["--import"] = parse_import;
1325 cmdmap["-geometry"] = parse_geometry;
1327 for (int i = 1; i < argc; ++i) {
1328 std::map<string, cmd_helper>::const_iterator it
1329 = cmdmap.find(argv[i]);
1331 // don't complain if not found - may be parsed later
1332 if (it == cmdmap.end())
1335 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1336 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1338 int const remove = 1 + it->second(arg, arg2);
1340 // Now, remove used arguments by shifting
1341 // the following ones remove places down.
1344 for (int j = i; j < argc; ++j)
1345 argv[j] = argv[j + remove];
1350 batch_command = batch;
1354 FuncStatus getStatus(FuncRequest const & action)
1356 return LyX::ref().lyxFunc().getStatus(action);
1360 void dispatch(FuncRequest const & action)
1362 LyX::ref().lyxFunc().dispatch(action);
1366 BufferList & theBufferList()
1368 return LyX::ref().bufferList();
1372 LyXFunc & theLyXFunc()
1374 return LyX::ref().lyxFunc();
1378 LyXServer & theLyXServer()
1380 // FIXME: this should not be use_gui dependent
1381 BOOST_ASSERT(use_gui);
1382 return LyX::ref().server();
1386 LyXServerSocket & theLyXServerSocket()
1388 // FIXME: this should not be use_gui dependent
1389 BOOST_ASSERT(use_gui);
1390 return LyX::ref().socket();
1394 kb_keymap & theTopLevelKeymap()
1396 BOOST_ASSERT(use_gui);
1397 return LyX::ref().topLevelKeymap();
1401 IconvProcessor & utf8ToUcs4()
1403 return LyX::ref().iconvProcessor();