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));
377 /* Create a CoreApplication class that will provide the main event loop
378 * and the socket callback registering. With Qt4, only QtCore
379 * library would be needed.
380 * When this is done, a server_mode could be created and the following two
381 * line would be moved out from here.
383 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
384 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
385 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
387 // Parse and remove all known arguments in the LyX singleton
388 // Give an error for all remaining ones.
389 int exit_status = init(argc, argv);
391 // Kill the application object before exiting.
392 pimpl_->application_.reset();
398 // Start the real execution loop.
399 exit_status = pimpl_->application_->exec();
403 // Restore original font resources after Application is destroyed.
404 support::restoreFontResources();
410 void LyX::prepareExit()
412 // Set a flag that we do quitting from the program,
413 // so no refreshes are necessary.
416 // close buffers first
417 pimpl_->buffer_list_.closeAll();
419 // do any other cleanup procedures now
420 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
422 // Prevent the deletion of /tmp if LyX was called with invalid
423 // arguments. Does not work on windows.
424 // FIXME: Fix the real bug instead.
425 if (package().temp_dir() == "/tmp") {
426 lyxerr << "Not deleting /tmp." << endl;
430 if (!destroyDir(FileName(package().temp_dir()))) {
431 docstring const msg =
432 bformat(_("Unable to remove the temporary directory %1$s"),
433 from_utf8(package().temp_dir()));
434 Alert::warning(_("Unable to remove temporary directory"), msg);
438 if (pimpl_->session_)
439 pimpl_->session_->writeFile();
440 pimpl_->session_.reset();
441 pimpl_->lyx_server_.reset();
442 pimpl_->lyx_socket_.reset();
445 // Kill the application object before exiting. This avoids crashes
446 // when exiting on Linux.
447 if (pimpl_->application_)
448 pimpl_->application_.reset();
452 void LyX::earlyExit(int status)
454 BOOST_ASSERT(pimpl_->application_.get());
455 // LyX::pimpl_::application_ is not initialised at this
456 // point so it's safe to just exit after some cleanup.
462 int LyX::init(int & argc, char * argv[])
464 // check for any spurious extra arguments
465 // other than documents
466 for (int argi = 1; argi < argc ; ++argi) {
467 if (argv[argi][0] == '-') {
469 bformat(_("Wrong command line option `%1$s'. Exiting."),
470 from_utf8(argv[argi]))) << endl;
475 // Initialization of LyX (reads lyxrc and more)
476 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
477 bool success = init();
478 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
482 for (int argi = argc - 1; argi >= 1; --argi) {
483 // get absolute path of file and add ".lyx" to
484 // the filename if necessary
485 pimpl_->files_to_load_.push_back(fileSearch(string(),
486 os::internal_path(to_utf8(from_local8bit(argv[argi]))), "lyx"));
490 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
496 void LyX::loadFiles()
498 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
499 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
501 for (; it != end; ++it) {
505 Buffer * const b = newFile(it->absFilename(), string(), true);
506 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
507 if (loadLyXFile(buf, *it)) {
508 ErrorList const & el = buf->errorList("Parse");
510 for_each(el.begin(), el.end(),
511 boost::bind(&LyX::printError, this, _1));
514 pimpl_->buffer_list_.release(buf);
519 void LyX::execBatchCommands()
521 // The advantage of doing this here is that the event loop
522 // is already started. So any need for interaction will be
526 // Execute batch commands if available
527 if (batch_command.empty())
530 lyxerr[Debug::INIT] << "About to handle -x '"
531 << batch_command << '\'' << endl;
533 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
537 void LyX::restoreGuiSession()
539 LyXView * view = newLyXView();
541 // if some files were specified at command-line we assume that the
542 // user wants to edit *these* files and not to restore the session.
543 if (!pimpl_->files_to_load_.empty()) {
544 for_each(pimpl_->files_to_load_.begin(),
545 pimpl_->files_to_load_.end(),
546 bind(&LyXView::loadLyXFile, view, _1, true));
547 // clear this list to save a few bytes of RAM
548 pimpl_->files_to_load_.clear();
549 pimpl_->session_->lastOpened().clear();
553 if (!lyxrc.load_session)
556 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
557 // do not add to the lastfile list since these files are restored from
558 // last session, and should be already there (regular files), or should
559 // not be added at all (help files).
560 for_each(lastopened.begin(), lastopened.end(),
561 bind(&LyXView::loadLyXFile, view, _1, false));
563 // clear this list to save a few bytes of RAM
564 pimpl_->session_->lastOpened().clear();
568 LyXView * LyX::newLyXView()
573 // determine windows size and position, from lyxrc and/or session
575 unsigned int width = 690;
576 unsigned int height = 510;
577 // default icon size, will be overwritten by stored session value
578 unsigned int iconSizeXY = 0;
579 bool maximize = false;
581 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
582 width = lyxrc.geometry_width;
583 height = lyxrc.geometry_height;
585 // if lyxrc returns (0,0), then use session info
587 string val = session().sessionInfo().load("WindowWidth");
589 width = convert<unsigned int>(val);
590 val = session().sessionInfo().load("WindowHeight");
592 height = convert<unsigned int>(val);
593 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
595 val = session().sessionInfo().load("IconSizeXY");
597 iconSizeXY = convert<unsigned int>(val);
600 // if user wants to restore window position
603 if (lyxrc.geometry_xysaved) {
604 string val = session().sessionInfo().load("WindowPosX");
606 posx = convert<int>(val);
607 val = session().sessionInfo().load("WindowPosY");
609 posy = convert<int>(val);
612 if (!geometryArg.empty())
618 // create the main window
619 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
627 The SIGHUP signal does not exist on Windows and does not need to be handled.
629 Windows handles SIGFPE and SIGSEGV signals as expected.
631 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
632 cause a new thread to be spawned. This may well result in unexpected
633 behaviour by the single-threaded LyX.
635 SIGTERM signals will come only from another process actually sending
636 that signal using 'raise' in Windows' POSIX compatability layer. It will
637 not come from the general "terminate process" methods that everyone
638 actually uses (and which can't be trapped). Killing an app 'politely' on
639 Windows involves first sending a WM_CLOSE message, something that is
640 caught already by the Qt frontend.
642 For more information see:
644 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
645 ...signals are mostly useless on Windows for a variety of reasons that are
648 'UNIX Application Migration Guide, Chapter 9'
649 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
651 'How To Terminate an Application "Cleanly" in Win32'
652 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
656 static void error_handler(int err_sig)
658 // Throw away any signals other than the first one received.
659 static sig_atomic_t handling_error = false;
662 handling_error = true;
664 // We have received a signal indicating a fatal error, so
665 // try and save the data ASAP.
666 LyX::cref().emergencyCleanup();
668 // These lyxerr calls may or may not work:
670 // Signals are asynchronous, so the main program may be in a very
671 // fragile state when a signal is processed and thus while a signal
672 // handler function executes.
673 // In general, therefore, we should avoid performing any
674 // I/O operations or calling most library and system functions from
677 // This shouldn't matter here, however, as we've already invoked
682 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
686 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
689 lyxerr << "\nlyx: SIGSEGV signal caught\n"
690 "Sorry, you have found a bug in LyX. "
691 "Please read the bug-reporting instructions "
692 "in Help->Introduction and send us a bug report, "
693 "if necessary. Thanks !\nBye." << endl;
701 // Deinstall the signal handlers
703 signal(SIGHUP, SIG_DFL);
705 signal(SIGINT, SIG_DFL);
706 signal(SIGFPE, SIG_DFL);
707 signal(SIGSEGV, SIG_DFL);
708 signal(SIGTERM, SIG_DFL);
711 if (err_sig == SIGSEGV ||
712 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
714 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
723 void LyX::printError(ErrorItem const & ei)
725 docstring tmp = _("LyX: ") + ei.error + char_type(':')
727 std::cerr << to_utf8(tmp) << std::endl;
731 void LyX::initGuiFont()
733 if (lyxrc.roman_font_name.empty())
734 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
736 if (lyxrc.sans_font_name.empty())
737 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
739 if (lyxrc.typewriter_font_name.empty())
740 lyxrc.typewriter_font_name
741 = pimpl_->application_->typewriterFontName();
748 signal(SIGHUP, error_handler);
750 signal(SIGFPE, error_handler);
751 signal(SIGSEGV, error_handler);
752 signal(SIGINT, error_handler);
753 signal(SIGTERM, error_handler);
754 // SIGPIPE can be safely ignored.
756 lyxrc.tempdir_path = package().temp_dir();
757 lyxrc.document_path = package().document_dir();
759 if (lyxrc.template_path.empty()) {
760 lyxrc.template_path = addPath(package().system_support(),
765 // Read configuration files
768 // This one may have been distributed along with LyX.
769 if (!readRcFile("lyxrc.dist"))
772 // Set the PATH correctly.
773 #if !defined (USE_POSIX_PACKAGING)
774 // Add the directory containing the LyX executable to the path
775 // so that LyX can find things like tex2lyx.
776 if (package().build_support().empty())
777 prependEnvPath("PATH", package().binary_dir());
779 if (!lyxrc.path_prefix.empty())
780 prependEnvPath("PATH", lyxrc.path_prefix);
782 // Check that user LyX directory is ok.
783 if (queryUserLyXDir(package().explicit_user_support()))
784 reconfigureUserLyXDir();
786 // no need for a splash when there is no GUI
791 // This one is generated in user_support directory by lib/configure.py.
792 if (!readRcFile("lyxrc.defaults"))
795 // Query the OS to know what formats are viewed natively
796 formats.setAutoOpen();
798 // Read lyxrc.dist again to be able to override viewer auto-detection.
799 readRcFile("lyxrc.dist");
801 system_lyxrc = lyxrc;
802 system_formats = formats;
803 system_converters = converters;
804 system_movers = movers;
805 system_lcolor = lcolor;
807 // This one is edited through the preferences dialog.
808 if (!readRcFile("preferences"))
811 if (!readEncodingsFile("encodings"))
813 if (!readLanguagesFile("languages"))
817 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
823 pimpl_->toplevel_keymap_.reset(new kb_keymap);
824 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
825 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
827 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
830 if (!readUIFile(lyxrc.ui_file))
834 if (lyxerr.debugging(Debug::LYXRC))
837 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
838 if (!lyxrc.path_prefix.empty())
839 prependEnvPath("PATH", lyxrc.path_prefix);
841 FileName const document_path(lyxrc.document_path);
842 if (fs::exists(document_path.toFilesystemEncoding()) &&
843 fs::is_directory(document_path.toFilesystemEncoding()))
844 package().document_dir() = lyxrc.document_path;
846 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
847 if (package().temp_dir().empty()) {
848 Alert::error(_("Could not create temporary directory"),
849 bformat(_("Could not create a temporary directory in\n"
850 "%1$s. Make sure that this\n"
851 "path exists and is writable and try again."),
852 from_utf8(lyxrc.tempdir_path)));
853 // createLyXTmpDir() tries sufficiently hard to create a
854 // usable temp dir, so the probability to come here is
855 // close to zero. We therefore don't try to overcome this
856 // problem with e.g. asking the user for a new path and
857 // trying again but simply exit.
861 if (lyxerr.debugging(Debug::INIT)) {
862 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
865 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
866 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
868 // This must happen after package initialization and after lyxrc is
869 // read, therefore it can't be done by a static object.
870 ConverterCache::init();
876 void LyX::defaultKeyBindings(kb_keymap * kbmap)
878 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
879 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
880 kbmap->bind("Up", FuncRequest(LFUN_UP));
881 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
883 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
884 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
885 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
886 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
888 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
889 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
890 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
891 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
893 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
894 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
896 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
897 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
899 // kbmap->bindings to enable the use of the numeric keypad
901 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
902 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
903 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
904 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
906 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
907 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
915 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
916 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
917 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
918 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
919 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
920 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
921 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
922 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
923 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
924 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
928 void LyX::emergencyCleanup() const
930 // what to do about tmpfiles is non-obvious. we would
931 // like to delete any we find, but our lyxdir might
932 // contain documents etc. which might be helpful on
935 pimpl_->buffer_list_.emergencyWriteAll();
937 if (pimpl_->lyx_server_)
938 pimpl_->lyx_server_->emergencyCleanup();
939 pimpl_->lyx_server_.reset();
940 pimpl_->lyx_socket_.reset();
945 void LyX::deadKeyBindings(kb_keymap * kbmap)
947 // bindKeyings for transparent handling of deadkeys
948 // The keysyms are gotten from XFree86 X11R6
949 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
950 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
951 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
952 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
953 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
954 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
955 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
956 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
957 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
958 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
959 // nothing with this name
960 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
961 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
962 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
963 // nothing with this name either...
964 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
965 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
966 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
967 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
973 // return true if file does not exist or is older than configure.py.
974 bool needsUpdate(string const & file)
976 // We cannot initialize configure_script directly because the package
977 // is not initialized yet when static objects are constructed.
978 static string configure_script;
979 static bool firstrun = true;
981 configure_script = FileName(addName(
982 package().system_support(),
983 "configure.py")).toFilesystemEncoding();
987 string const absfile = FileName(addName(
988 package().user_support(), file)).toFilesystemEncoding();
989 return (! fs::exists(absfile))
990 || (fs::last_write_time(configure_script)
991 > fs::last_write_time(absfile));
997 bool LyX::queryUserLyXDir(bool explicit_userdir)
999 // Does user directory exist?
1000 string const user_support =
1001 FileName(package().user_support()).toFilesystemEncoding();
1002 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1003 first_start = false;
1005 return needsUpdate("lyxrc.defaults")
1006 || needsUpdate("textclass.lst")
1007 || needsUpdate("packages.lst");
1010 first_start = !explicit_userdir;
1012 // If the user specified explicitly a directory, ask whether
1013 // to create it. If the user says "no", then exit.
1014 if (explicit_userdir &&
1016 _("Missing user LyX directory"),
1017 bformat(_("You have specified a non-existent user "
1018 "LyX directory, %1$s.\n"
1019 "It is needed to keep your own configuration."),
1020 from_utf8(package().user_support())),
1022 _("&Create directory"),
1024 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1025 earlyExit(EXIT_FAILURE);
1028 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1029 from_utf8(package().user_support())))
1032 if (!createDirectory(package().user_support(), 0755)) {
1033 // Failed, so let's exit.
1034 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1036 earlyExit(EXIT_FAILURE);
1043 bool LyX::readRcFile(string const & name)
1045 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1047 FileName const lyxrc_path = libFileSearch(string(), name);
1048 if (!lyxrc_path.empty()) {
1050 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1052 if (lyxrc.read(lyxrc_path) < 0) {
1053 showFileError(name);
1057 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1063 // Read the ui file `name'
1064 bool LyX::readUIFile(string const & name)
1074 struct keyword_item uitags[ui_last - 1] = {
1075 { "include", ui_include },
1076 { "menuset", ui_menuset },
1077 { "toolbar", ui_toolbar },
1078 { "toolbars", ui_toolbars }
1081 // Ensure that a file is read only once (prevents include loops)
1082 static std::list<string> uifiles;
1083 std::list<string>::const_iterator it = uifiles.begin();
1084 std::list<string>::const_iterator end = uifiles.end();
1085 it = std::find(it, end, name);
1087 lyxerr[Debug::INIT] << "UI file '" << name
1088 << "' has been read already. "
1089 << "Is this an include loop?"
1094 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1096 FileName const ui_path = libFileSearch("ui", name, "ui");
1098 if (ui_path.empty()) {
1099 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1100 showFileError(name);
1103 uifiles.push_back(name);
1105 lyxerr[Debug::INIT] << "Found " << name
1106 << " in " << ui_path << endl;
1107 LyXLex lex(uitags, ui_last - 1);
1108 lex.setFile(ui_path);
1110 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1114 if (lyxerr.debugging(Debug::PARSER))
1115 lex.printTable(lyxerr);
1117 while (lex.isOK()) {
1118 switch (lex.lex()) {
1121 string const file = lex.getString();
1122 if (!readUIFile(file))
1127 menubackend.read(lex);
1131 toolbarbackend.read(lex);
1135 toolbarbackend.readToolbars(lex);
1139 if (!rtrim(lex.getString()).empty())
1140 lex.printError("LyX::ReadUIFile: "
1141 "Unknown menu tag: `$$Token'");
1149 // Read the languages file `name'
1150 bool LyX::readLanguagesFile(string const & name)
1152 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1154 FileName const lang_path = libFileSearch(string(), name);
1155 if (lang_path.empty()) {
1156 showFileError(name);
1159 languages.read(lang_path);
1164 // Read the encodings file `name'
1165 bool LyX::readEncodingsFile(string const & name)
1167 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1169 FileName const enc_path = libFileSearch(string(), name);
1170 if (enc_path.empty()) {
1171 showFileError(name);
1174 encodings.read(enc_path);
1183 /// return the the number of arguments consumed
1184 typedef boost::function<int(string const &, string const &)> cmd_helper;
1186 int parse_dbg(string const & arg, string const &)
1189 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1190 Debug::showTags(lyxerr);
1193 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1195 lyxerr.level(Debug::value(arg));
1196 Debug::showLevel(lyxerr, lyxerr.level());
1201 int parse_help(string const &, string const &)
1204 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1205 "Command line switches (case sensitive):\n"
1206 "\t-help summarize LyX usage\n"
1207 "\t-userdir dir set user directory to dir\n"
1208 "\t-sysdir dir set system directory to dir\n"
1209 "\t-geometry WxH+X+Y set geometry of the main window\n"
1210 "\t-dbg feature[,feature]...\n"
1211 " select the features to debug.\n"
1212 " Type `lyx -dbg' to see the list of features\n"
1213 "\t-x [--execute] command\n"
1214 " where command is a lyx command.\n"
1215 "\t-e [--export] fmt\n"
1216 " where fmt is the export format of choice.\n"
1217 "\t-i [--import] fmt file.xxx\n"
1218 " where fmt is the import format of choice\n"
1219 " and file.xxx is the file to be imported.\n"
1220 "\t-version summarize version and build info\n"
1221 "Check the LyX man page for more details.")) << endl;
1226 int parse_version(string const &, string const &)
1228 lyxerr << "LyX " << lyx_version
1229 << " (" << lyx_release_date << ")" << endl;
1230 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1232 lyxerr << lyx_version_info << endl;
1237 int parse_sysdir(string const & arg, string const &)
1240 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1243 cl_system_support = arg;
1247 int parse_userdir(string const & arg, string const &)
1250 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1253 cl_user_support = arg;
1257 int parse_execute(string const & arg, string const &)
1260 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1267 int parse_export(string const & type, string const &)
1270 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1271 "--export switch")) << endl;
1274 batch = "buffer-export " + type;
1279 int parse_import(string const & type, string const & file)
1282 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1283 "--import switch")) << endl;
1287 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1291 batch = "buffer-import " + type + ' ' + file;
1295 int parse_geometry(string const & arg1, string const &)
1298 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1299 // remove also the arg
1302 // don't remove "-geometry"
1311 void LyX::easyParse(int & argc, char * argv[])
1313 std::map<string, cmd_helper> cmdmap;
1315 cmdmap["-dbg"] = parse_dbg;
1316 cmdmap["-help"] = parse_help;
1317 cmdmap["--help"] = parse_help;
1318 cmdmap["-version"] = parse_version;
1319 cmdmap["--version"] = parse_version;
1320 cmdmap["-sysdir"] = parse_sysdir;
1321 cmdmap["-userdir"] = parse_userdir;
1322 cmdmap["-x"] = parse_execute;
1323 cmdmap["--execute"] = parse_execute;
1324 cmdmap["-e"] = parse_export;
1325 cmdmap["--export"] = parse_export;
1326 cmdmap["-i"] = parse_import;
1327 cmdmap["--import"] = parse_import;
1328 cmdmap["-geometry"] = parse_geometry;
1330 for (int i = 1; i < argc; ++i) {
1331 std::map<string, cmd_helper>::const_iterator it
1332 = cmdmap.find(argv[i]);
1334 // don't complain if not found - may be parsed later
1335 if (it == cmdmap.end())
1338 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1339 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1341 int const remove = 1 + it->second(arg, arg2);
1343 // Now, remove used arguments by shifting
1344 // the following ones remove places down.
1347 for (int j = i; j < argc; ++j)
1348 argv[j] = argv[j + remove];
1353 batch_command = batch;
1357 FuncStatus getStatus(FuncRequest const & action)
1359 return LyX::ref().lyxFunc().getStatus(action);
1363 void dispatch(FuncRequest const & action)
1365 LyX::ref().lyxFunc().dispatch(action);
1369 BufferList & theBufferList()
1371 return LyX::ref().bufferList();
1375 LyXFunc & theLyXFunc()
1377 return LyX::ref().lyxFunc();
1381 LyXServer & theLyXServer()
1383 // FIXME: this should not be use_gui dependent
1384 BOOST_ASSERT(use_gui);
1385 return LyX::ref().server();
1389 LyXServerSocket & theLyXServerSocket()
1391 // FIXME: this should not be use_gui dependent
1392 BOOST_ASSERT(use_gui);
1393 return LyX::ref().socket();
1397 kb_keymap & theTopLevelKeymap()
1399 BOOST_ASSERT(use_gui);
1400 return LyX::ref().topLevelKeymap();
1404 IconvProcessor & utf8ToUcs4()
1406 return LyX::ref().iconvProcessor();