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>
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 frontend::Application * theApp = 0;
105 /// are we using the GUI at all?
107 * We default to true and this is changed to false when the export feature is used.
114 // Filled with the command line arguments "foo" of "-sysdir foo" or
116 string cl_system_support;
117 string cl_user_support;
119 std::string geometryArg;
121 LyX * singleton_ = 0;
123 void showFileError(string const & error)
125 Alert::warning(_("Could not read configuration file"),
126 bformat(_("Error while reading the configuration file\n%1$s.\n"
127 "Please check your installation."), from_utf8(error)));
131 void reconfigureUserLyXDir()
133 string const configure_command = package().configure_command();
135 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
136 support::Path p(package().user_support());
138 one.startscript(Systemcall::Wait, configure_command);
139 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
145 /// The main application class private implementation.
146 struct LyX::Singletons
148 Singletons(): iconv(ucs4_codeset, "UTF-8")
151 /// our function handler
154 BufferList buffer_list_;
156 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
158 boost::scoped_ptr<LyXServer> lyx_server_;
160 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
162 boost::scoped_ptr<frontend::Application> application_;
163 /// lyx session, containing lastfiles, lastfilepos, and lastopened
164 boost::scoped_ptr<Session> session_;
167 IconvProcessor iconv;
173 // Static data are not treated in the same way at all on the Mac (and
174 // the LyX singleton has static methods). This is the reason why the
175 // exit command on the Mac bypasses our dispatch machinery altogether.
176 // On Linux and Windows we won't pass a second time through quit()
177 // because quitting will already be set to true.
185 BOOST_ASSERT(singleton_);
190 LyX const & LyX::cref()
192 BOOST_ASSERT(singleton_);
201 pimpl_.reset(new Singletons);
206 BufferList & LyX::bufferList()
208 return pimpl_->buffer_list_;
212 BufferList const & LyX::bufferList() const
214 return pimpl_->buffer_list_;
218 Session & LyX::session()
220 BOOST_ASSERT(pimpl_->session_.get());
221 return *pimpl_->session_.get();
225 Session const & LyX::session() const
227 BOOST_ASSERT(pimpl_->session_.get());
228 return *pimpl_->session_.get();
232 LyXFunc & LyX::lyxFunc()
234 return pimpl_->lyxfunc_;
238 LyXFunc const & LyX::lyxFunc() const
240 return pimpl_->lyxfunc_;
244 LyXServer & LyX::server()
246 BOOST_ASSERT(pimpl_->lyx_server_.get());
247 return *pimpl_->lyx_server_.get();
251 LyXServer const & LyX::server() const
253 BOOST_ASSERT(pimpl_->lyx_server_.get());
254 return *pimpl_->lyx_server_.get();
258 LyXServerSocket & LyX::socket()
260 BOOST_ASSERT(pimpl_->lyx_socket_.get());
261 return *pimpl_->lyx_socket_.get();
265 LyXServerSocket const & LyX::socket() const
267 BOOST_ASSERT(pimpl_->lyx_socket_.get());
268 return *pimpl_->lyx_socket_.get();
272 frontend::Application & LyX::application()
274 BOOST_ASSERT(pimpl_->application_.get());
275 return *pimpl_->application_.get();
279 frontend::Application const & LyX::application() const
281 BOOST_ASSERT(pimpl_->application_.get());
282 return *pimpl_->application_.get();
286 kb_keymap & LyX::topLevelKeymap()
288 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
289 return *pimpl_->toplevel_keymap_.get();
293 IconvProcessor & LyX::iconvProcessor()
295 return pimpl_->iconv;
299 kb_keymap const & LyX::topLevelKeymap() const
301 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
302 return *pimpl_->toplevel_keymap_.get();
306 Buffer const * const LyX::updateInset(InsetBase const * inset) const
311 Buffer const * buffer_ptr = 0;
312 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
313 vector<int>::const_iterator it = view_ids.begin();
314 vector<int>::const_iterator const end = view_ids.end();
315 for (; it != end; ++it) {
317 pimpl_->application_->gui().view(*it).updateInset(inset);
325 int LyX::exec(int & argc, char * argv[])
327 // Here we need to parse the command line. At least
328 // we need to parse for "-dbg" and "-help"
329 easyParse(argc, argv);
331 support::init_package(argv[0], cl_system_support, cl_user_support,
332 support::top_build_dir_is_one_level_up);
334 vector<string> files;
337 // FIXME: create a ConsoleApplication
338 int exit_status = loadFiles(argc, argv, files);
344 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
349 BufferList::iterator begin = pimpl_->buffer_list_.begin();
350 BufferList::iterator end = pimpl_->buffer_list_.end();
352 bool final_success = false;
353 for (BufferList::iterator I = begin; I != end; ++I) {
355 bool success = false;
356 buf->dispatch(batch_command, &success);
357 final_success |= success;
360 return !final_success;
363 // Force adding of font path _before_ Application is initialized
364 support::addFontResources();
366 // Let the frontend parse and remove all arguments that it knows
367 pimpl_->application_.reset(createApplication(argc, argv));
368 // FIXME: this global pointer should probably go.
369 theApp = pimpl_->application_.get();
373 // Parse and remove all known arguments in the LyX singleton
374 // Give an error for all remaining ones.
375 int exit_status = loadFiles(argc, argv, files);
377 // Kill the application object before exiting.
378 pimpl_->application_.reset();
384 restoreGuiSession(files);
385 // Start the real execution loop.
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 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
395 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
396 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
398 exit_status = pimpl_->application_->exec();
399 // Kill the application object before exiting. This avoid crash
401 pimpl_->application_.reset();
402 // Restore original font resources after Application is destroyed.
403 support::restoreFontResources();
409 void LyX::prepareExit()
411 // Set a flag that we do quitting from the program,
412 // so no refreshes are necessary.
415 // close buffers first
416 pimpl_->buffer_list_.closeAll();
418 // do any other cleanup procedures now
419 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
421 // Prevent the deletion of /tmp if LyX was called with invalid
422 // arguments. Does not work on windows.
423 // FIXME: Fix the real bug instead.
424 if (package().temp_dir() == "/tmp") {
425 lyxerr << "Not deleting /tmp." << endl;
429 if (!destroyDir(package().temp_dir())) {
430 docstring const msg =
431 bformat(_("Unable to remove the temporary directory %1$s"),
432 from_utf8(package().temp_dir()));
433 Alert::warning(_("Unable to remove temporary directory"), msg);
438 void LyX::earlyExit(int status)
440 BOOST_ASSERT(pimpl_->application_.get());
441 // LyX::pimpl_::application_ is not initialised at this
442 // point so it's safe to just exit after some cleanup.
450 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
454 if (pimpl_->session_)
455 pimpl_->session_->writeFile();
456 pimpl_->lyx_server_.reset();
457 pimpl_->lyx_socket_.reset();
458 if (pimpl_->application_)
459 pimpl_->application_->exit(0);
465 int LyX::loadFiles(int & argc, char * argv[],
466 vector<string> & files)
468 // check for any spurious extra arguments
469 // other than documents
470 for (int argi = 1; argi < argc ; ++argi) {
471 if (argv[argi][0] == '-') {
473 bformat(_("Wrong command line option `%1$s'. Exiting."),
474 from_utf8(argv[argi]))) << endl;
479 // Initialization of LyX (reads lyxrc and more)
480 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
481 bool success = init();
482 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
486 for (int argi = argc - 1; argi >= 1; --argi) {
487 // check for any remaining extra arguments other than
488 // document file names. These will be passed out to the
490 if (argv[argi][0] == '-')
492 files.push_back(os::internal_path(argv[argi]));
496 files.push_back(i18nLibFileSearch("examples", "splash.lyx").absFilename());
498 Buffer * last_loaded = 0;
500 vector<string>::const_iterator it = files.begin();
501 vector<string>::const_iterator end = files.end();
503 for (; it != end; ++it) {
504 // get absolute path of file and add ".lyx" to
505 // the filename if necessary
506 string s = fileSearch(string(), *it, "lyx").absFilename();
508 Buffer * const b = newFile(*it, string(), true);
512 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
513 if (loadLyXFile(buf, s)) {
515 ErrorList const & el = buf->errorList("Parse");
517 for_each(el.begin(), el.end(),
518 boost::bind(&LyX::printError, this, _1));
521 pimpl_->buffer_list_.release(buf);
525 files.clear(); // the files are already loaded
531 void LyX::execBatchCommands()
533 // Execute batch commands if available
534 if (batch_command.empty())
537 lyxerr[Debug::INIT] << "About to handle -x '"
538 << batch_command << '\'' << endl;
540 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
544 void LyX::restoreGuiSession(vector<string> const & files)
546 LyXView * view = newLyXView();
549 for_each(files.begin(), files.end(),
550 bind(&LyXView::loadLyXFile, view, _1, true));
552 // if a file is specified, I assume that user wants to edit *that* file
553 if (files.empty() && lyxrc.load_session) {
554 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
555 // do not add to the lastfile list since these files are restored from
556 // last seesion, and should be already there (regular files), or should
557 // not be added at all (help files).
558 for_each(lastopened.begin(), lastopened.end(),
559 bind(&LyXView::loadLyXFile, view, _1, false));
561 // clear this list to save a few bytes of RAM
562 pimpl_->session_->lastOpened().clear();
566 LyXView * LyX::newLyXView()
571 // determine windows size and position, from lyxrc and/or session
573 unsigned int width = 690;
574 unsigned int height = 510;
575 // default icon size, will be overwritten by stored session value
576 unsigned int iconSizeXY = 0;
577 bool maximize = false;
579 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
580 width = lyxrc.geometry_width;
581 height = lyxrc.geometry_height;
583 // if lyxrc returns (0,0), then use session info
585 string val = session().sessionInfo().load("WindowWidth");
587 width = convert<unsigned int>(val);
588 val = session().sessionInfo().load("WindowHeight");
590 height = convert<unsigned int>(val);
591 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
593 val = session().sessionInfo().load("IconSizeXY");
595 iconSizeXY = convert<unsigned int>(val);
598 // if user wants to restore window position
601 if (lyxrc.geometry_xysaved) {
602 string val = session().sessionInfo().load("WindowPosX");
604 posx = convert<int>(val);
605 val = session().sessionInfo().load("WindowPosY");
607 posy = convert<int>(val);
610 if (!geometryArg.empty())
616 // create the main window
617 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
625 The SIGHUP signal does not exist on Windows and does not need to be handled.
627 Windows handles SIGFPE and SIGSEGV signals as expected.
629 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
630 cause a new thread to be spawned. This may well result in unexpected
631 behaviour by the single-threaded LyX.
633 SIGTERM signals will come only from another process actually sending
634 that signal using 'raise' in Windows' POSIX compatability layer. It will
635 not come from the general "terminate process" methods that everyone
636 actually uses (and which can't be trapped). Killing an app 'politely' on
637 Windows involves first sending a WM_CLOSE message, something that is
638 caught already by the Qt frontend.
640 For more information see:
642 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
643 ...signals are mostly useless on Windows for a variety of reasons that are
646 'UNIX Application Migration Guide, Chapter 9'
647 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
649 'How To Terminate an Application "Cleanly" in Win32'
650 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
654 static void error_handler(int err_sig)
656 // Throw away any signals other than the first one received.
657 static sig_atomic_t handling_error = false;
660 handling_error = true;
662 // We have received a signal indicating a fatal error, so
663 // try and save the data ASAP.
664 LyX::cref().emergencyCleanup();
666 // These lyxerr calls may or may not work:
668 // Signals are asynchronous, so the main program may be in a very
669 // fragile state when a signal is processed and thus while a signal
670 // handler function executes.
671 // In general, therefore, we should avoid performing any
672 // I/O operations or calling most library and system functions from
675 // This shouldn't matter here, however, as we've already invoked
680 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
684 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
687 lyxerr << "\nlyx: SIGSEGV signal caught\n"
688 "Sorry, you have found a bug in LyX. "
689 "Please read the bug-reporting instructions "
690 "in Help->Introduction and send us a bug report, "
691 "if necessary. Thanks !\nBye." << endl;
699 // Deinstall the signal handlers
701 signal(SIGHUP, SIG_DFL);
703 signal(SIGINT, SIG_DFL);
704 signal(SIGFPE, SIG_DFL);
705 signal(SIGSEGV, SIG_DFL);
706 signal(SIGTERM, SIG_DFL);
709 if (err_sig == SIGSEGV ||
710 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
712 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
721 void LyX::printError(ErrorItem const & ei)
723 docstring tmp = _("LyX: ") + ei.error + char_type(':')
725 std::cerr << to_utf8(tmp) << std::endl;
729 void LyX::initGuiFont()
731 if (lyxrc.roman_font_name.empty())
732 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
734 if (lyxrc.sans_font_name.empty())
735 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
737 if (lyxrc.typewriter_font_name.empty())
738 lyxrc.typewriter_font_name
739 = pimpl_->application_->typewriterFontName();
746 signal(SIGHUP, error_handler);
748 signal(SIGFPE, error_handler);
749 signal(SIGSEGV, error_handler);
750 signal(SIGINT, error_handler);
751 signal(SIGTERM, error_handler);
752 // SIGPIPE can be safely ignored.
754 lyxrc.tempdir_path = package().temp_dir();
755 lyxrc.document_path = package().document_dir();
757 if (lyxrc.template_path.empty()) {
758 lyxrc.template_path = addPath(package().system_support(),
763 // Read configuration files
766 // This one may have been distributed along with LyX.
767 if (!readRcFile("lyxrc.dist"))
770 // Set the PATH correctly.
771 #if !defined (USE_POSIX_PACKAGING)
772 // Add the directory containing the LyX executable to the path
773 // so that LyX can find things like tex2lyx.
774 if (package().build_support().empty())
775 prependEnvPath("PATH", package().binary_dir());
777 if (!lyxrc.path_prefix.empty())
778 prependEnvPath("PATH", lyxrc.path_prefix);
780 // Check that user LyX directory is ok.
781 if (queryUserLyXDir(package().explicit_user_support()))
782 reconfigureUserLyXDir();
784 // no need for a splash when there is no GUI
789 // This one is generated in user_support directory by lib/configure.py.
790 if (!readRcFile("lyxrc.defaults"))
793 // Query the OS to know what formats are viewed natively
794 formats.setAutoOpen();
796 // Read lyxrc.dist again to be able to override viewer auto-detection.
797 readRcFile("lyxrc.dist");
799 system_lyxrc = lyxrc;
800 system_formats = formats;
801 system_converters = converters;
802 system_movers = movers;
803 system_lcolor = lcolor;
805 // This one is edited through the preferences dialog.
806 if (!readRcFile("preferences"))
809 if (!readEncodingsFile("encodings"))
811 if (!readLanguagesFile("languages"))
815 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
821 pimpl_->toplevel_keymap_.reset(new kb_keymap);
822 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
823 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
825 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
828 if (!readUIFile(lyxrc.ui_file))
832 if (lyxerr.debugging(Debug::LYXRC))
835 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
836 if (!lyxrc.path_prefix.empty())
837 prependEnvPath("PATH", lyxrc.path_prefix);
839 if (fs::exists(lyxrc.document_path) &&
840 fs::is_directory(lyxrc.document_path))
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 static string const configure_script =
974 addName(package().system_support(), "configure.py");
975 string const absfile =
976 addName(package().user_support(), file);
978 return (! fs::exists(absfile))
979 || (fs::last_write_time(configure_script)
980 > fs::last_write_time(absfile));
986 bool LyX::queryUserLyXDir(bool explicit_userdir)
988 // Does user directory exist?
989 if (fs::exists(package().user_support()) &&
990 fs::is_directory(package().user_support())) {
993 return needsUpdate("lyxrc.defaults")
994 || needsUpdate("textclass.lst")
995 || needsUpdate("packages.lst");
998 first_start = !explicit_userdir;
1000 // If the user specified explicitly a directory, ask whether
1001 // to create it. If the user says "no", then exit.
1002 if (explicit_userdir &&
1004 _("Missing user LyX directory"),
1005 bformat(_("You have specified a non-existent user "
1006 "LyX directory, %1$s.\n"
1007 "It is needed to keep your own configuration."),
1008 from_utf8(package().user_support())),
1010 _("&Create directory"),
1012 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1013 earlyExit(EXIT_FAILURE);
1016 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1017 from_utf8(package().user_support())))
1020 if (!createDirectory(package().user_support(), 0755)) {
1021 // Failed, so let's exit.
1022 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1024 earlyExit(EXIT_FAILURE);
1031 bool LyX::readRcFile(string const & name)
1033 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1035 FileName const lyxrc_path = libFileSearch(string(), name);
1036 if (!lyxrc_path.empty()) {
1038 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1040 if (lyxrc.read(lyxrc_path) < 0) {
1041 showFileError(name);
1045 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1051 // Read the ui file `name'
1052 bool LyX::readUIFile(string const & name)
1062 struct keyword_item uitags[ui_last - 1] = {
1063 { "include", ui_include },
1064 { "menuset", ui_menuset },
1065 { "toolbar", ui_toolbar },
1066 { "toolbars", ui_toolbars }
1069 // Ensure that a file is read only once (prevents include loops)
1070 static std::list<string> uifiles;
1071 std::list<string>::const_iterator it = uifiles.begin();
1072 std::list<string>::const_iterator end = uifiles.end();
1073 it = std::find(it, end, name);
1075 lyxerr[Debug::INIT] << "UI file '" << name
1076 << "' has been read already. "
1077 << "Is this an include loop?"
1082 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1084 FileName const ui_path = libFileSearch("ui", name, "ui");
1086 if (ui_path.empty()) {
1087 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1088 showFileError(name);
1091 uifiles.push_back(name);
1093 lyxerr[Debug::INIT] << "Found " << name
1094 << " in " << ui_path << endl;
1095 LyXLex lex(uitags, ui_last - 1);
1096 lex.setFile(ui_path);
1098 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1102 if (lyxerr.debugging(Debug::PARSER))
1103 lex.printTable(lyxerr);
1105 while (lex.isOK()) {
1106 switch (lex.lex()) {
1109 string const file = lex.getString();
1110 if (!readUIFile(file))
1115 menubackend.read(lex);
1119 toolbarbackend.read(lex);
1123 toolbarbackend.readToolbars(lex);
1127 if (!rtrim(lex.getString()).empty())
1128 lex.printError("LyX::ReadUIFile: "
1129 "Unknown menu tag: `$$Token'");
1137 // Read the languages file `name'
1138 bool LyX::readLanguagesFile(string const & name)
1140 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1142 FileName const lang_path = libFileSearch(string(), name);
1143 if (lang_path.empty()) {
1144 showFileError(name);
1147 languages.read(lang_path);
1152 // Read the encodings file `name'
1153 bool LyX::readEncodingsFile(string const & name)
1155 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1157 FileName const enc_path = libFileSearch(string(), name);
1158 if (enc_path.empty()) {
1159 showFileError(name);
1162 encodings.read(enc_path);
1171 /// return the the number of arguments consumed
1172 typedef boost::function<int(string const &, string const &)> cmd_helper;
1174 int parse_dbg(string const & arg, string const &)
1177 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1178 Debug::showTags(lyxerr);
1181 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1183 lyxerr.level(Debug::value(arg));
1184 Debug::showLevel(lyxerr, lyxerr.level());
1189 int parse_help(string const &, string const &)
1192 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1193 "Command line switches (case sensitive):\n"
1194 "\t-help summarize LyX usage\n"
1195 "\t-userdir dir set user directory to dir\n"
1196 "\t-sysdir dir set system directory to dir\n"
1197 "\t-geometry WxH+X+Y set geometry of the main window\n"
1198 "\t-dbg feature[,feature]...\n"
1199 " select the features to debug.\n"
1200 " Type `lyx -dbg' to see the list of features\n"
1201 "\t-x [--execute] command\n"
1202 " where command is a lyx command.\n"
1203 "\t-e [--export] fmt\n"
1204 " where fmt is the export format of choice.\n"
1205 "\t-i [--import] fmt file.xxx\n"
1206 " where fmt is the import format of choice\n"
1207 " and file.xxx is the file to be imported.\n"
1208 "\t-version summarize version and build info\n"
1209 "Check the LyX man page for more details.")) << endl;
1214 int parse_version(string const &, string const &)
1216 lyxerr << "LyX " << lyx_version
1217 << " (" << lyx_release_date << ")" << endl;
1218 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1220 lyxerr << lyx_version_info << endl;
1225 int parse_sysdir(string const & arg, string const &)
1228 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1231 cl_system_support = arg;
1235 int parse_userdir(string const & arg, string const &)
1238 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1241 cl_user_support = arg;
1245 int parse_execute(string const & arg, string const &)
1248 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1255 int parse_export(string const & type, string const &)
1258 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1259 "--export switch")) << endl;
1262 batch = "buffer-export " + type;
1267 int parse_import(string const & type, string const & file)
1270 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1271 "--import switch")) << endl;
1275 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1279 batch = "buffer-import " + type + ' ' + file;
1283 int parse_geometry(string const & arg1, string const &)
1286 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1287 // remove also the arg
1290 // don't remove "-geometry"
1299 void LyX::easyParse(int & argc, char * argv[])
1301 std::map<string, cmd_helper> cmdmap;
1303 cmdmap["-dbg"] = parse_dbg;
1304 cmdmap["-help"] = parse_help;
1305 cmdmap["--help"] = parse_help;
1306 cmdmap["-version"] = parse_version;
1307 cmdmap["--version"] = parse_version;
1308 cmdmap["-sysdir"] = parse_sysdir;
1309 cmdmap["-userdir"] = parse_userdir;
1310 cmdmap["-x"] = parse_execute;
1311 cmdmap["--execute"] = parse_execute;
1312 cmdmap["-e"] = parse_export;
1313 cmdmap["--export"] = parse_export;
1314 cmdmap["-i"] = parse_import;
1315 cmdmap["--import"] = parse_import;
1316 cmdmap["-geometry"] = parse_geometry;
1318 for (int i = 1; i < argc; ++i) {
1319 std::map<string, cmd_helper>::const_iterator it
1320 = cmdmap.find(argv[i]);
1322 // don't complain if not found - may be parsed later
1323 if (it == cmdmap.end())
1326 string arg((i + 1 < argc) ? argv[i + 1] : "");
1327 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1329 int const remove = 1 + it->second(arg, arg2);
1331 // Now, remove used arguments by shifting
1332 // the following ones remove places down.
1335 for (int j = i; j < argc; ++j)
1336 argv[j] = argv[j + remove];
1341 batch_command = batch;
1345 FuncStatus getStatus(FuncRequest const & action)
1347 return LyX::ref().lyxFunc().getStatus(action);
1351 void dispatch(FuncRequest const & action)
1353 LyX::ref().lyxFunc().dispatch(action);
1357 BufferList & theBufferList()
1359 return LyX::ref().bufferList();
1363 LyXFunc & theLyXFunc()
1365 return LyX::ref().lyxFunc();
1369 LyXServer & theLyXServer()
1371 // FIXME: this should not be use_gui dependent
1372 BOOST_ASSERT(use_gui);
1373 return LyX::ref().server();
1377 LyXServerSocket & theLyXServerSocket()
1379 // FIXME: this should not be use_gui dependent
1380 BOOST_ASSERT(use_gui);
1381 return LyX::ref().socket();
1385 kb_keymap & theTopLevelKeymap()
1387 BOOST_ASSERT(use_gui);
1388 return LyX::ref().topLevelKeymap();
1392 IconvProcessor & utf8ToUcs4()
1394 return LyX::ref().iconvProcessor();