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/lyxlib.h"
54 #include "support/convert.h"
55 #include "support/os.h"
56 #include "support/package.h"
57 #include "support/path.h"
58 #include "support/systemcall.h"
59 #include "support/unicode.h"
61 #include <boost/bind.hpp>
62 #include <boost/filesystem/operations.hpp>
71 using support::addName;
72 using support::addPath;
73 using support::bformat;
74 using support::createDirectory;
75 using support::createLyXTmpDir;
76 using support::destroyDir;
77 using support::FileName;
78 using support::fileSearch;
79 using support::getEnv;
80 using support::i18nLibFileSearch;
81 using support::libFileSearch;
82 using support::package;
83 using support::prependEnvPath;
85 using support::Systemcall;
87 namespace Alert = frontend::Alert;
88 namespace os = support::os;
89 namespace fs = boost::filesystem;
96 #ifndef CXX_GLOBAL_CSTD
103 /// are we using the GUI at all?
105 * We default to true and this is changed to false when the export feature is used.
112 // Filled with the command line arguments "foo" of "-sysdir foo" or
114 string cl_system_support;
115 string cl_user_support;
117 std::string geometryArg;
119 LyX * singleton_ = 0;
121 void showFileError(string const & error)
123 Alert::warning(_("Could not read configuration file"),
124 bformat(_("Error while reading the configuration file\n%1$s.\n"
125 "Please check your installation."), from_utf8(error)));
129 void reconfigureUserLyXDir()
131 string const configure_command = package().configure_command();
133 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
134 support::Path p(package().user_support());
136 one.startscript(Systemcall::Wait, configure_command);
137 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
143 /// The main application class private implementation.
144 struct LyX::Singletons
146 Singletons(): iconv(ucs4_codeset, "UTF-8")
149 /// our function handler
152 BufferList buffer_list_;
154 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
156 boost::scoped_ptr<LyXServer> lyx_server_;
158 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
160 boost::scoped_ptr<frontend::Application> application_;
161 /// lyx session, containing lastfiles, lastfilepos, and lastopened
162 boost::scoped_ptr<Session> session_;
165 IconvProcessor iconv;
167 /// Files to load at start.
168 vector<FileName> files_to_load_;
172 frontend::Application * theApp()
175 return &singleton_->application();
188 BOOST_ASSERT(singleton_);
193 LyX const & LyX::cref()
195 BOOST_ASSERT(singleton_);
204 pimpl_.reset(new Singletons);
209 BufferList & LyX::bufferList()
211 return pimpl_->buffer_list_;
215 BufferList const & LyX::bufferList() const
217 return pimpl_->buffer_list_;
221 Session & LyX::session()
223 BOOST_ASSERT(pimpl_->session_.get());
224 return *pimpl_->session_.get();
228 Session const & LyX::session() const
230 BOOST_ASSERT(pimpl_->session_.get());
231 return *pimpl_->session_.get();
235 LyXFunc & LyX::lyxFunc()
237 return pimpl_->lyxfunc_;
241 LyXFunc const & LyX::lyxFunc() const
243 return pimpl_->lyxfunc_;
247 LyXServer & LyX::server()
249 BOOST_ASSERT(pimpl_->lyx_server_.get());
250 return *pimpl_->lyx_server_.get();
254 LyXServer const & LyX::server() const
256 BOOST_ASSERT(pimpl_->lyx_server_.get());
257 return *pimpl_->lyx_server_.get();
261 LyXServerSocket & LyX::socket()
263 BOOST_ASSERT(pimpl_->lyx_socket_.get());
264 return *pimpl_->lyx_socket_.get();
268 LyXServerSocket const & LyX::socket() const
270 BOOST_ASSERT(pimpl_->lyx_socket_.get());
271 return *pimpl_->lyx_socket_.get();
275 frontend::Application & LyX::application()
277 BOOST_ASSERT(pimpl_->application_.get());
278 return *pimpl_->application_.get();
282 frontend::Application const & LyX::application() const
284 BOOST_ASSERT(pimpl_->application_.get());
285 return *pimpl_->application_.get();
289 kb_keymap & LyX::topLevelKeymap()
291 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
292 return *pimpl_->toplevel_keymap_.get();
296 IconvProcessor & LyX::iconvProcessor()
298 return pimpl_->iconv;
302 kb_keymap const & LyX::topLevelKeymap() const
304 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
305 return *pimpl_->toplevel_keymap_.get();
309 Buffer const * const LyX::updateInset(InsetBase const * inset) const
314 Buffer const * buffer_ptr = 0;
315 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
316 vector<int>::const_iterator it = view_ids.begin();
317 vector<int>::const_iterator const end = view_ids.end();
318 for (; it != end; ++it) {
320 pimpl_->application_->gui().view(*it).updateInset(inset);
328 int LyX::exec(int & argc, char * argv[])
330 // Here we need to parse the command line. At least
331 // we need to parse for "-dbg" and "-help"
332 easyParse(argc, argv);
334 support::init_package(to_utf8(from_local8bit(argv[0])),
335 cl_system_support, cl_user_support,
336 support::top_build_dir_is_one_level_up);
339 // FIXME: create a ConsoleApplication
340 int exit_status = init(argc, argv);
348 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
353 BufferList::iterator begin = pimpl_->buffer_list_.begin();
354 BufferList::iterator end = pimpl_->buffer_list_.end();
356 bool final_success = false;
357 for (BufferList::iterator I = begin; I != end; ++I) {
359 bool success = false;
360 buf->dispatch(batch_command, &success);
361 final_success |= success;
364 return !final_success;
367 // Force adding of font path _before_ Application is initialized
368 support::os::addFontResources();
370 // Let the frontend parse and remove all arguments that it knows
371 pimpl_->application_.reset(createApplication(argc, argv));
375 // Parse and remove all known arguments in the LyX singleton
376 // Give an error for all remaining ones.
377 int exit_status = init(argc, argv);
379 // Kill the application object before exiting.
380 pimpl_->application_.reset();
387 /* Create a CoreApplication class that will provide the main event loop
388 * and the socket callback registering. With Qt4, only QtCore
389 * library would be needed.
390 * When this is done, a server_mode could be created and the following two
391 * line would be moved out from here.
393 // Note: socket callback must be registered after init(argc, argv)
394 // such that package().temp_dir() is properly initialized.
395 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
396 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
397 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
399 // Start the real execution loop.
400 exit_status = pimpl_->application_->exec();
404 // Restore original font resources after Application is destroyed.
405 support::os::restoreFontResources();
411 void LyX::prepareExit()
413 // Set a flag that we do quitting from the program,
414 // so no refreshes are necessary.
417 // close buffers first
418 pimpl_->buffer_list_.closeAll();
420 // do any other cleanup procedures now
421 if (package().temp_dir() != package().system_temp_dir()) {
422 lyxerr[Debug::INFO] << "Deleting tmp dir "
423 << package().temp_dir() << endl;
425 if (!destroyDir(FileName(package().temp_dir()))) {
426 docstring const msg =
427 bformat(_("Unable to remove the temporary directory %1$s"),
428 from_utf8(package().temp_dir()));
429 Alert::warning(_("Unable to remove temporary directory"), msg);
434 if (pimpl_->session_)
435 pimpl_->session_->writeFile();
436 pimpl_->session_.reset();
437 pimpl_->lyx_server_.reset();
438 pimpl_->lyx_socket_.reset();
441 // Kill the application object before exiting. This avoids crashes
442 // when exiting on Linux.
443 if (pimpl_->application_)
444 pimpl_->application_.reset();
448 void LyX::earlyExit(int status)
450 BOOST_ASSERT(pimpl_->application_.get());
451 // LyX::pimpl_::application_ is not initialised at this
452 // point so it's safe to just exit after some cleanup.
458 int LyX::init(int & argc, char * argv[])
460 // check for any spurious extra arguments
461 // other than documents
462 for (int argi = 1; argi < argc ; ++argi) {
463 if (argv[argi][0] == '-') {
465 bformat(_("Wrong command line option `%1$s'. Exiting."),
466 from_utf8(argv[argi]))) << endl;
471 // Initialization of LyX (reads lyxrc and more)
472 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
473 bool success = init();
474 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
478 for (int argi = argc - 1; argi >= 1; --argi) {
479 // get absolute path of file and add ".lyx" to
480 // the filename if necessary
481 pimpl_->files_to_load_.push_back(fileSearch(string(),
482 os::internal_path(to_utf8(from_local8bit(argv[argi]))),
483 "lyx", support::allow_unreadable));
487 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
493 void LyX::loadFiles()
495 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
496 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
498 for (; it != end; ++it) {
502 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
503 if (loadLyXFile(buf, *it)) {
504 ErrorList const & el = buf->errorList("Parse");
506 for_each(el.begin(), el.end(),
507 boost::bind(&LyX::printError, this, _1));
510 pimpl_->buffer_list_.release(buf);
515 void LyX::execBatchCommands()
517 // The advantage of doing this here is that the event loop
518 // is already started. So any need for interaction will be
522 // Execute batch commands if available
523 if (batch_command.empty())
526 lyxerr[Debug::INIT] << "About to handle -x '"
527 << batch_command << '\'' << endl;
529 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
533 void LyX::restoreGuiSession()
535 LyXView * view = newLyXView();
537 // if some files were specified at command-line we assume that the
538 // user wants to edit *these* files and not to restore the session.
539 if (!pimpl_->files_to_load_.empty()) {
540 for_each(pimpl_->files_to_load_.begin(),
541 pimpl_->files_to_load_.end(),
542 bind(&LyXView::loadLyXFile, view, _1, true));
543 // clear this list to save a few bytes of RAM
544 pimpl_->files_to_load_.clear();
545 pimpl_->session_->lastOpened().clear();
549 if (!lyxrc.load_session)
552 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
553 // do not add to the lastfile list since these files are restored from
554 // last session, and should be already there (regular files), or should
555 // not be added at all (help files).
556 for_each(lastopened.begin(), lastopened.end(),
557 bind(&LyXView::loadLyXFile, view, _1, false));
559 // clear this list to save a few bytes of RAM
560 pimpl_->session_->lastOpened().clear();
564 LyXView * LyX::newLyXView()
569 // determine windows size and position, from lyxrc and/or session
571 unsigned int width = 690;
572 unsigned int height = 510;
573 // default icon size, will be overwritten by stored session value
574 unsigned int iconSizeXY = 0;
575 bool maximize = false;
577 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
578 width = lyxrc.geometry_width;
579 height = lyxrc.geometry_height;
581 // if lyxrc returns (0,0), then use session info
583 string val = session().sessionInfo().load("WindowWidth");
585 width = convert<unsigned int>(val);
586 val = session().sessionInfo().load("WindowHeight");
588 height = convert<unsigned int>(val);
589 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
591 val = session().sessionInfo().load("IconSizeXY");
593 iconSizeXY = convert<unsigned int>(val);
596 // if user wants to restore window position
599 if (lyxrc.geometry_xysaved) {
600 string val = session().sessionInfo().load("WindowPosX");
602 posx = convert<int>(val);
603 val = session().sessionInfo().load("WindowPosY");
605 posy = convert<int>(val);
608 if (!geometryArg.empty())
614 // create the main window
615 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
623 The SIGHUP signal does not exist on Windows and does not need to be handled.
625 Windows handles SIGFPE and SIGSEGV signals as expected.
627 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
628 cause a new thread to be spawned. This may well result in unexpected
629 behaviour by the single-threaded LyX.
631 SIGTERM signals will come only from another process actually sending
632 that signal using 'raise' in Windows' POSIX compatability layer. It will
633 not come from the general "terminate process" methods that everyone
634 actually uses (and which can't be trapped). Killing an app 'politely' on
635 Windows involves first sending a WM_CLOSE message, something that is
636 caught already by the Qt frontend.
638 For more information see:
640 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
641 ...signals are mostly useless on Windows for a variety of reasons that are
644 'UNIX Application Migration Guide, Chapter 9'
645 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
647 'How To Terminate an Application "Cleanly" in Win32'
648 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
652 static void error_handler(int err_sig)
654 // Throw away any signals other than the first one received.
655 static sig_atomic_t handling_error = false;
658 handling_error = true;
660 // We have received a signal indicating a fatal error, so
661 // try and save the data ASAP.
662 LyX::cref().emergencyCleanup();
664 // These lyxerr calls may or may not work:
666 // Signals are asynchronous, so the main program may be in a very
667 // fragile state when a signal is processed and thus while a signal
668 // handler function executes.
669 // In general, therefore, we should avoid performing any
670 // I/O operations or calling most library and system functions from
673 // This shouldn't matter here, however, as we've already invoked
678 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
682 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
685 lyxerr << "\nlyx: SIGSEGV signal caught\n"
686 "Sorry, you have found a bug in LyX. "
687 "Please read the bug-reporting instructions "
688 "in Help->Introduction and send us a bug report, "
689 "if necessary. Thanks !\nBye." << endl;
697 // Deinstall the signal handlers
699 signal(SIGHUP, SIG_DFL);
701 signal(SIGINT, SIG_DFL);
702 signal(SIGFPE, SIG_DFL);
703 signal(SIGSEGV, SIG_DFL);
704 signal(SIGTERM, SIG_DFL);
707 if (err_sig == SIGSEGV ||
708 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
710 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
719 void LyX::printError(ErrorItem const & ei)
721 docstring tmp = _("LyX: ") + ei.error + char_type(':')
723 std::cerr << to_utf8(tmp) << std::endl;
727 void LyX::initGuiFont()
729 if (lyxrc.roman_font_name.empty())
730 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
732 if (lyxrc.sans_font_name.empty())
733 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
735 if (lyxrc.typewriter_font_name.empty())
736 lyxrc.typewriter_font_name
737 = pimpl_->application_->typewriterFontName();
744 signal(SIGHUP, error_handler);
746 signal(SIGFPE, error_handler);
747 signal(SIGSEGV, error_handler);
748 signal(SIGINT, error_handler);
749 signal(SIGTERM, error_handler);
750 // SIGPIPE can be safely ignored.
752 lyxrc.tempdir_path = package().temp_dir();
753 lyxrc.document_path = package().document_dir();
755 if (lyxrc.template_path.empty()) {
756 lyxrc.template_path = addPath(package().system_support(),
761 // Read configuration files
764 // This one may have been distributed along with LyX.
765 if (!readRcFile("lyxrc.dist"))
768 // Set the PATH correctly.
769 #if !defined (USE_POSIX_PACKAGING)
770 // Add the directory containing the LyX executable to the path
771 // so that LyX can find things like tex2lyx.
772 if (package().build_support().empty())
773 prependEnvPath("PATH", package().binary_dir());
775 if (!lyxrc.path_prefix.empty())
776 prependEnvPath("PATH", lyxrc.path_prefix);
778 // Check that user LyX directory is ok.
779 if (queryUserLyXDir(package().explicit_user_support()))
780 reconfigureUserLyXDir();
782 // no need for a splash when there is no GUI
787 // This one is generated in user_support directory by lib/configure.py.
788 if (!readRcFile("lyxrc.defaults"))
791 // Query the OS to know what formats are viewed natively
792 formats.setAutoOpen();
794 // Read lyxrc.dist again to be able to override viewer auto-detection.
795 readRcFile("lyxrc.dist");
797 system_lyxrc = lyxrc;
798 system_formats = formats;
799 system_converters = converters;
800 system_movers = movers;
801 system_lcolor = lcolor;
803 // This one is edited through the preferences dialog.
804 if (!readRcFile("preferences"))
807 if (!readEncodingsFile("encodings"))
809 if (!readLanguagesFile("languages"))
813 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
819 pimpl_->toplevel_keymap_.reset(new kb_keymap);
820 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
821 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
823 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
826 if (!readUIFile(lyxrc.ui_file))
830 if (lyxerr.debugging(Debug::LYXRC))
833 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
834 if (!lyxrc.path_prefix.empty())
835 prependEnvPath("PATH", lyxrc.path_prefix);
837 FileName const document_path(lyxrc.document_path);
838 if (fs::exists(document_path.toFilesystemEncoding()) &&
839 fs::is_directory(document_path.toFilesystemEncoding()))
840 package().document_dir() = lyxrc.document_path;
842 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
843 if (package().temp_dir().empty()) {
844 Alert::error(_("Could not create temporary directory"),
845 bformat(_("Could not create a temporary directory in\n"
846 "%1$s. Make sure that this\n"
847 "path exists and is writable and try again."),
848 from_utf8(lyxrc.tempdir_path)));
849 // createLyXTmpDir() tries sufficiently hard to create a
850 // usable temp dir, so the probability to come here is
851 // close to zero. We therefore don't try to overcome this
852 // problem with e.g. asking the user for a new path and
853 // trying again but simply exit.
857 if (lyxerr.debugging(Debug::INIT)) {
858 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
861 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
862 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
864 // This must happen after package initialization and after lyxrc is
865 // read, therefore it can't be done by a static object.
866 ConverterCache::init();
872 void LyX::defaultKeyBindings(kb_keymap * kbmap)
874 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
875 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
876 kbmap->bind("Up", FuncRequest(LFUN_UP));
877 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
879 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
880 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
881 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
882 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
884 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
885 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
886 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
887 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
889 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
890 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
892 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
893 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
895 // kbmap->bindings to enable the use of the numeric keypad
897 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
898 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
899 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
900 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
901 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
902 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
903 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
904 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
906 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
913 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
914 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
915 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
916 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
917 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
918 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
919 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
920 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
924 void LyX::emergencyCleanup() const
926 // what to do about tmpfiles is non-obvious. we would
927 // like to delete any we find, but our lyxdir might
928 // contain documents etc. which might be helpful on
931 pimpl_->buffer_list_.emergencyWriteAll();
933 if (pimpl_->lyx_server_)
934 pimpl_->lyx_server_->emergencyCleanup();
935 pimpl_->lyx_server_.reset();
936 pimpl_->lyx_socket_.reset();
941 void LyX::deadKeyBindings(kb_keymap * kbmap)
943 // bindKeyings for transparent handling of deadkeys
944 // The keysyms are gotten from XFree86 X11R6
945 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
946 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
947 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
948 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
949 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
950 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
951 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
952 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
953 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
954 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
955 // nothing with this name
956 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
957 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
958 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
959 // nothing with this name either...
960 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
961 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
962 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
963 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
969 // return true if file does not exist or is older than configure.py.
970 bool needsUpdate(string const & file)
972 // We cannot initialize configure_script directly because the package
973 // is not initialized yet when static objects are constructed.
974 static string configure_script;
975 static bool firstrun = true;
977 configure_script = FileName(addName(
978 package().system_support(),
979 "configure.py")).toFilesystemEncoding();
983 string const absfile = FileName(addName(
984 package().user_support(), file)).toFilesystemEncoding();
985 return (! fs::exists(absfile))
986 || (fs::last_write_time(configure_script)
987 > fs::last_write_time(absfile));
993 bool LyX::queryUserLyXDir(bool explicit_userdir)
995 // Does user directory exist?
996 string const user_support =
997 FileName(package().user_support()).toFilesystemEncoding();
998 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1001 return needsUpdate("lyxrc.defaults")
1002 || needsUpdate("textclass.lst")
1003 || needsUpdate("packages.lst");
1006 first_start = !explicit_userdir;
1008 // If the user specified explicitly a directory, ask whether
1009 // to create it. If the user says "no", then exit.
1010 if (explicit_userdir &&
1012 _("Missing user LyX directory"),
1013 bformat(_("You have specified a non-existent user "
1014 "LyX directory, %1$s.\n"
1015 "It is needed to keep your own configuration."),
1016 from_utf8(package().user_support())),
1018 _("&Create directory"),
1020 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1021 earlyExit(EXIT_FAILURE);
1024 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1025 from_utf8(package().user_support())))
1028 if (!createDirectory(package().user_support(), 0755)) {
1029 // Failed, so let's exit.
1030 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1032 earlyExit(EXIT_FAILURE);
1039 bool LyX::readRcFile(string const & name)
1041 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1043 FileName const lyxrc_path = libFileSearch(string(), name);
1044 if (!lyxrc_path.empty()) {
1046 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1048 if (lyxrc.read(lyxrc_path) < 0) {
1049 showFileError(name);
1053 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1059 // Read the ui file `name'
1060 bool LyX::readUIFile(string const & name)
1070 struct keyword_item uitags[ui_last - 1] = {
1071 { "include", ui_include },
1072 { "menuset", ui_menuset },
1073 { "toolbar", ui_toolbar },
1074 { "toolbars", ui_toolbars }
1077 // Ensure that a file is read only once (prevents include loops)
1078 static std::list<string> uifiles;
1079 std::list<string>::const_iterator it = uifiles.begin();
1080 std::list<string>::const_iterator end = uifiles.end();
1081 it = std::find(it, end, name);
1083 lyxerr[Debug::INIT] << "UI file '" << name
1084 << "' has been read already. "
1085 << "Is this an include loop?"
1090 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1092 FileName const ui_path = libFileSearch("ui", name, "ui");
1094 if (ui_path.empty()) {
1095 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1096 showFileError(name);
1099 uifiles.push_back(name);
1101 lyxerr[Debug::INIT] << "Found " << name
1102 << " in " << ui_path << endl;
1103 LyXLex lex(uitags, ui_last - 1);
1104 lex.setFile(ui_path);
1106 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1110 if (lyxerr.debugging(Debug::PARSER))
1111 lex.printTable(lyxerr);
1113 while (lex.isOK()) {
1114 switch (lex.lex()) {
1117 string const file = lex.getString();
1118 if (!readUIFile(file))
1123 menubackend.read(lex);
1127 toolbarbackend.read(lex);
1131 toolbarbackend.readToolbars(lex);
1135 if (!rtrim(lex.getString()).empty())
1136 lex.printError("LyX::ReadUIFile: "
1137 "Unknown menu tag: `$$Token'");
1145 // Read the languages file `name'
1146 bool LyX::readLanguagesFile(string const & name)
1148 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1150 FileName const lang_path = libFileSearch(string(), name);
1151 if (lang_path.empty()) {
1152 showFileError(name);
1155 languages.read(lang_path);
1160 // Read the encodings file `name'
1161 bool LyX::readEncodingsFile(string const & name)
1163 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1165 FileName const enc_path = libFileSearch(string(), name);
1166 if (enc_path.empty()) {
1167 showFileError(name);
1170 encodings.read(enc_path);
1179 /// return the the number of arguments consumed
1180 typedef boost::function<int(string const &, string const &)> cmd_helper;
1182 int parse_dbg(string const & arg, string const &)
1185 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1186 Debug::showTags(lyxerr);
1189 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1191 lyxerr.level(Debug::value(arg));
1192 Debug::showLevel(lyxerr, lyxerr.level());
1197 int parse_help(string const &, string const &)
1200 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1201 "Command line switches (case sensitive):\n"
1202 "\t-help summarize LyX usage\n"
1203 "\t-userdir dir set user directory to dir\n"
1204 "\t-sysdir dir set system directory to dir\n"
1205 "\t-geometry WxH+X+Y set geometry of the main window\n"
1206 "\t-dbg feature[,feature]...\n"
1207 " select the features to debug.\n"
1208 " Type `lyx -dbg' to see the list of features\n"
1209 "\t-x [--execute] command\n"
1210 " where command is a lyx command.\n"
1211 "\t-e [--export] fmt\n"
1212 " where fmt is the export format of choice.\n"
1213 "\t-i [--import] fmt file.xxx\n"
1214 " where fmt is the import format of choice\n"
1215 " and file.xxx is the file to be imported.\n"
1216 "\t-version summarize version and build info\n"
1217 "Check the LyX man page for more details.")) << endl;
1222 int parse_version(string const &, string const &)
1224 lyxerr << "LyX " << lyx_version
1225 << " (" << lyx_release_date << ")" << endl;
1226 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1228 lyxerr << lyx_version_info << endl;
1233 int parse_sysdir(string const & arg, string const &)
1236 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1239 cl_system_support = arg;
1243 int parse_userdir(string const & arg, string const &)
1246 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1249 cl_user_support = arg;
1253 int parse_execute(string const & arg, string const &)
1256 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1263 int parse_export(string const & type, string const &)
1266 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1267 "--export switch")) << endl;
1270 batch = "buffer-export " + type;
1275 int parse_import(string const & type, string const & file)
1278 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1279 "--import switch")) << endl;
1283 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1287 batch = "buffer-import " + type + ' ' + file;
1291 int parse_geometry(string const & arg1, string const &)
1294 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1295 // remove also the arg
1298 // don't remove "-geometry"
1307 void LyX::easyParse(int & argc, char * argv[])
1309 std::map<string, cmd_helper> cmdmap;
1311 cmdmap["-dbg"] = parse_dbg;
1312 cmdmap["-help"] = parse_help;
1313 cmdmap["--help"] = parse_help;
1314 cmdmap["-version"] = parse_version;
1315 cmdmap["--version"] = parse_version;
1316 cmdmap["-sysdir"] = parse_sysdir;
1317 cmdmap["-userdir"] = parse_userdir;
1318 cmdmap["-x"] = parse_execute;
1319 cmdmap["--execute"] = parse_execute;
1320 cmdmap["-e"] = parse_export;
1321 cmdmap["--export"] = parse_export;
1322 cmdmap["-i"] = parse_import;
1323 cmdmap["--import"] = parse_import;
1324 cmdmap["-geometry"] = parse_geometry;
1326 for (int i = 1; i < argc; ++i) {
1327 std::map<string, cmd_helper>::const_iterator it
1328 = cmdmap.find(argv[i]);
1330 // don't complain if not found - may be parsed later
1331 if (it == cmdmap.end())
1334 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1335 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1337 int const remove = 1 + it->second(arg, arg2);
1339 // Now, remove used arguments by shifting
1340 // the following ones remove places down.
1343 for (int j = i; j < argc; ++j)
1344 argv[j] = argv[j + remove];
1349 batch_command = batch;
1353 FuncStatus getStatus(FuncRequest const & action)
1355 return LyX::ref().lyxFunc().getStatus(action);
1359 void dispatch(FuncRequest const & action)
1361 LyX::ref().lyxFunc().dispatch(action);
1365 BufferList & theBufferList()
1367 return LyX::ref().bufferList();
1371 LyXFunc & theLyXFunc()
1373 return LyX::ref().lyxFunc();
1377 LyXServer & theLyXServer()
1379 // FIXME: this should not be use_gui dependent
1380 BOOST_ASSERT(use_gui);
1381 return LyX::ref().server();
1385 LyXServerSocket & theLyXServerSocket()
1387 // FIXME: this should not be use_gui dependent
1388 BOOST_ASSERT(use_gui);
1389 return LyX::ref().socket();
1393 kb_keymap & theTopLevelKeymap()
1395 BOOST_ASSERT(use_gui);
1396 return LyX::ref().topLevelKeymap();
1400 IconvProcessor & utf8ToUcs4()
1402 return LyX::ref().iconvProcessor();