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 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;
171 // Static data are not treated in the same way at all on the Mac (and
172 // the LyX singleton has static methods). This is the reason why the
173 // exit command on the Mac bypasses our dispatch machinery altogether.
174 // On Linux and Windows we won't pass a second time through quit()
175 // because quitting will already be set to true.
183 BOOST_ASSERT(singleton_);
188 LyX const & LyX::cref()
190 BOOST_ASSERT(singleton_);
196 : first_start(false), geometryOption_(false)
199 pimpl_.reset(new Singletons);
203 BufferList & LyX::bufferList()
205 return pimpl_->buffer_list_;
209 BufferList const & LyX::bufferList() const
211 return pimpl_->buffer_list_;
215 Session & LyX::session()
217 BOOST_ASSERT(pimpl_->session_.get());
218 return *pimpl_->session_.get();
222 Session const & LyX::session() const
224 BOOST_ASSERT(pimpl_->session_.get());
225 return *pimpl_->session_.get();
229 LyXFunc & LyX::lyxFunc()
231 return pimpl_->lyxfunc_;
235 LyXFunc const & LyX::lyxFunc() const
237 return pimpl_->lyxfunc_;
241 LyXServer & LyX::server()
243 BOOST_ASSERT(pimpl_->lyx_server_.get());
244 return *pimpl_->lyx_server_.get();
248 LyXServer const & LyX::server() const
250 BOOST_ASSERT(pimpl_->lyx_server_.get());
251 return *pimpl_->lyx_server_.get();
255 LyXServerSocket & LyX::socket()
257 BOOST_ASSERT(pimpl_->lyx_socket_.get());
258 return *pimpl_->lyx_socket_.get();
262 LyXServerSocket const & LyX::socket() const
264 BOOST_ASSERT(pimpl_->lyx_socket_.get());
265 return *pimpl_->lyx_socket_.get();
269 frontend::Application & LyX::application()
271 BOOST_ASSERT(pimpl_->application_.get());
272 return *pimpl_->application_.get();
276 frontend::Application const & LyX::application() const
278 BOOST_ASSERT(pimpl_->application_.get());
279 return *pimpl_->application_.get();
283 kb_keymap & LyX::topLevelKeymap()
285 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
286 return *pimpl_->toplevel_keymap_.get();
290 IconvProcessor & LyX::iconvProcessor()
292 return pimpl_->iconv;
296 kb_keymap const & LyX::topLevelKeymap() const
298 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
299 return *pimpl_->toplevel_keymap_.get();
303 Buffer const * const LyX::updateInset(InsetBase const * inset) const
308 Buffer const * buffer_ptr = 0;
309 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
310 vector<int>::const_iterator it = view_ids.begin();
311 vector<int>::const_iterator const end = view_ids.end();
312 for (; it != end; ++it) {
314 pimpl_->application_->gui().view(*it).updateInset(inset);
322 int LyX::exec(int & argc, char * argv[])
324 // Here we need to parse the command line. At least
325 // we need to parse for "-dbg" and "-help"
326 easyParse(argc, argv);
328 support::init_package(argv[0], cl_system_support, cl_user_support,
329 support::top_build_dir_is_one_level_up);
331 vector<string> files;
334 // FIXME: create a ConsoleApplication
335 int exit_status = loadFiles(argc, argv, files);
341 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
346 BufferList::iterator begin = pimpl_->buffer_list_.begin();
347 BufferList::iterator end = pimpl_->buffer_list_.end();
349 bool final_success = false;
350 for (BufferList::iterator I = begin; I != end; ++I) {
352 bool success = false;
353 buf->dispatch(batch_command, &success);
354 final_success |= success;
357 return !final_success;
360 // Force adding of font path _before_ Application is initialized
361 support::addFontResources();
363 // Let the frontend parse and remove all arguments that it knows
364 pimpl_->application_.reset(createApplication(argc, argv));
365 // FIXME: this global pointer should probably go.
366 theApp = pimpl_->application_.get();
370 // Parse and remove all known arguments in the LyX singleton
371 // Give an error for all remaining ones.
372 int exit_status = loadFiles(argc, argv, files);
374 // Kill the application object before exiting.
375 pimpl_->application_.reset();
381 restoreGuiSession(files);
382 // Start the real execution loop.
385 /* Create a CoreApplication class that will provide the main event loop
386 * and the socket callback registering. With Qt4, only QtCore
387 * library would be needed.
388 * When this is done, a server_mode could be created and the following two
389 * line would be moved out from here.
391 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
392 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
393 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
395 exit_status = pimpl_->application_->exec();
396 // Kill the application object before exiting. This avoid crash
398 pimpl_->application_.reset();
399 // Restore original font resources after Application is destroyed.
400 support::restoreFontResources();
406 void LyX::prepareExit()
408 // Set a flag that we do quitting from the program,
409 // so no refreshes are necessary.
412 // close buffers first
413 pimpl_->buffer_list_.closeAll();
415 // do any other cleanup procedures now
416 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
418 if (!destroyDir(package().temp_dir())) {
419 docstring const msg =
420 bformat(_("Unable to remove the temporary directory %1$s"),
421 from_utf8(package().temp_dir()));
422 Alert::warning(_("Unable to remove temporary directory"), msg);
427 void LyX::earlyExit(int status)
429 BOOST_ASSERT(pimpl_->application_.get());
430 // LyX::pimpl_::application_ is not initialised at this
431 // point so it's safe to just exit after some cleanup.
439 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
444 pimpl_->session_->writeFile();
445 pimpl_->lyx_server_.reset();
446 pimpl_->lyx_socket_.reset();
447 pimpl_->application_->exit(0);
453 int LyX::loadFiles(int & argc, char * argv[],
454 vector<string> & files)
456 // check for any spurious extra arguments
457 // other than documents
458 for (int argi = 1; argi < argc ; ++argi) {
459 if (argv[argi][0] == '-') {
461 bformat(_("Wrong command line option `%1$s'. Exiting."),
462 from_utf8(argv[argi]))) << endl;
467 // Initialization of LyX (reads lyxrc and more)
468 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
469 bool success = init();
470 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
474 for (int argi = argc - 1; argi >= 1; --argi) {
475 // check for any remaining extra arguments other than
476 // document file names. These will be passed out to the
478 if (argv[argi][0] == '-')
480 files.push_back(os::internal_path(argv[argi]));
484 files.push_back(i18nLibFileSearch("examples", "splash.lyx").absFilename());
486 Buffer * last_loaded = 0;
488 vector<string>::const_iterator it = files.begin();
489 vector<string>::const_iterator end = files.end();
491 for (; it != end; ++it) {
492 // get absolute path of file and add ".lyx" to
493 // the filename if necessary
494 string s = fileSearch(string(), *it, "lyx").absFilename();
496 Buffer * const b = newFile(*it, string(), true);
500 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
501 if (loadLyXFile(buf, s)) {
503 ErrorList const & el = buf->errorList("Parse");
505 for_each(el.begin(), el.end(),
506 boost::bind(&LyX::printError, this, _1));
509 pimpl_->buffer_list_.release(buf);
513 files.clear(); // the files are already loaded
519 void LyX::execBatchCommands()
521 // Execute batch commands if available
522 if (batch_command.empty())
525 lyxerr[Debug::INIT] << "About to handle -x '"
526 << batch_command << '\'' << endl;
528 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
532 void LyX::restoreGuiSession(vector<string> const & files)
534 LyXView * view = newLyXView();
537 for_each(files.begin(), files.end(),
538 bind(&LyXView::loadLyXFile, view, _1, true));
540 // if a file is specified, I assume that user wants to edit *that* file
541 if (files.empty() && lyxrc.load_session) {
542 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
543 // do not add to the lastfile list since these files are restored from
544 // last seesion, and should be already there (regular files), or should
545 // not be added at all (help files).
546 for_each(lastopened.begin(), lastopened.end(),
547 bind(&LyXView::loadLyXFile, view, _1, false));
549 // clear this list to save a few bytes of RAM
550 pimpl_->session_->lastOpened().clear();
554 LyXView * LyX::newLyXView()
559 // determine windows size and position, from lyxrc and/or session
561 unsigned int width = 690;
562 unsigned int height = 510;
563 // default icon size, will be overwritten by stored session value
564 unsigned int iconSizeXY = 26;
565 bool maximize = false;
567 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
568 width = lyxrc.geometry_width;
569 height = lyxrc.geometry_height;
571 // if lyxrc returns (0,0), then use session info
573 string val = session().sessionInfo().load("WindowWidth");
575 width = convert<unsigned int>(val);
576 val = session().sessionInfo().load("WindowHeight");
578 height = convert<unsigned int>(val);
579 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
581 val = session().sessionInfo().load("IconSizeXY");
583 iconSizeXY = convert<unsigned int>(val);
586 // if user wants to restore window position
589 if (lyxrc.geometry_xysaved) {
590 string val = session().sessionInfo().load("WindowPosX");
592 posx = convert<int>(val);
593 val = session().sessionInfo().load("WindowPosY");
595 posy = convert<int>(val);
598 if (geometryOption_) {
602 // create the main window
603 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY);
611 The SIGHUP signal does not exist on Windows and does not need to be handled.
613 Windows handles SIGFPE and SIGSEGV signals as expected.
615 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
616 cause a new thread to be spawned. This may well result in unexpected
617 behaviour by the single-threaded LyX.
619 SIGTERM signals will come only from another process actually sending
620 that signal using 'raise' in Windows' POSIX compatability layer. It will
621 not come from the general "terminate process" methods that everyone
622 actually uses (and which can't be trapped). Killing an app 'politely' on
623 Windows involves first sending a WM_CLOSE message, something that is
624 caught already by the Qt frontend.
626 For more information see:
628 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
629 ...signals are mostly useless on Windows for a variety of reasons that are
632 'UNIX Application Migration Guide, Chapter 9'
633 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
635 'How To Terminate an Application "Cleanly" in Win32'
636 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
640 static void error_handler(int err_sig)
642 // Throw away any signals other than the first one received.
643 static sig_atomic_t handling_error = false;
646 handling_error = true;
648 // We have received a signal indicating a fatal error, so
649 // try and save the data ASAP.
650 LyX::cref().emergencyCleanup();
652 // These lyxerr calls may or may not work:
654 // Signals are asynchronous, so the main program may be in a very
655 // fragile state when a signal is processed and thus while a signal
656 // handler function executes.
657 // In general, therefore, we should avoid performing any
658 // I/O operations or calling most library and system functions from
661 // This shouldn't matter here, however, as we've already invoked
666 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
670 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
673 lyxerr << "\nlyx: SIGSEGV signal caught\n"
674 "Sorry, you have found a bug in LyX. "
675 "Please read the bug-reporting instructions "
676 "in Help->Introduction and send us a bug report, "
677 "if necessary. Thanks !\nBye." << endl;
685 // Deinstall the signal handlers
687 signal(SIGHUP, SIG_DFL);
689 signal(SIGINT, SIG_DFL);
690 signal(SIGFPE, SIG_DFL);
691 signal(SIGSEGV, SIG_DFL);
692 signal(SIGTERM, SIG_DFL);
695 if (err_sig == SIGSEGV ||
696 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
698 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
707 void LyX::printError(ErrorItem const & ei)
709 docstring tmp = _("LyX: ") + ei.error + char_type(':')
711 std::cerr << to_utf8(tmp) << std::endl;
715 void LyX::initGuiFont()
717 if (lyxrc.roman_font_name.empty())
718 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
720 if (lyxrc.sans_font_name.empty())
721 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
723 if (lyxrc.typewriter_font_name.empty())
724 lyxrc.typewriter_font_name
725 = pimpl_->application_->typewriterFontName();
732 signal(SIGHUP, error_handler);
734 signal(SIGFPE, error_handler);
735 signal(SIGSEGV, error_handler);
736 signal(SIGINT, error_handler);
737 signal(SIGTERM, error_handler);
738 // SIGPIPE can be safely ignored.
740 lyxrc.tempdir_path = package().temp_dir();
741 lyxrc.document_path = package().document_dir();
743 if (lyxrc.template_path.empty()) {
744 lyxrc.template_path = addPath(package().system_support(),
749 // Read configuration files
752 // This one may have been distributed along with LyX.
753 if (!readRcFile("lyxrc.dist"))
756 // Set the PATH correctly.
757 #if !defined (USE_POSIX_PACKAGING)
758 // Add the directory containing the LyX executable to the path
759 // so that LyX can find things like tex2lyx.
760 if (package().build_support().empty())
761 prependEnvPath("PATH", package().binary_dir());
763 if (!lyxrc.path_prefix.empty())
764 prependEnvPath("PATH", lyxrc.path_prefix);
766 // Check that user LyX directory is ok.
767 if (queryUserLyXDir(package().explicit_user_support()))
768 reconfigureUserLyXDir();
770 // no need for a splash when there is no GUI
775 // This one is generated in user_support directory by lib/configure.py.
776 if (!readRcFile("lyxrc.defaults"))
779 // Query the OS to know what formats are viewed natively
780 formats.setAutoOpen();
782 // Read lyxrc.dist again to be able to override viewer auto-detection.
783 readRcFile("lyxrc.dist");
785 system_lyxrc = lyxrc;
786 system_formats = formats;
787 system_converters = converters;
788 system_movers = movers;
789 system_lcolor = lcolor;
791 // This one is edited through the preferences dialog.
792 if (!readRcFile("preferences"))
795 if (!readEncodingsFile("encodings"))
797 if (!readLanguagesFile("languages"))
801 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
807 pimpl_->toplevel_keymap_.reset(new kb_keymap);
808 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
809 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
811 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
814 if (!readUIFile(lyxrc.ui_file))
818 if (lyxerr.debugging(Debug::LYXRC))
821 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
822 if (!lyxrc.path_prefix.empty())
823 prependEnvPath("PATH", lyxrc.path_prefix);
825 if (fs::exists(lyxrc.document_path) &&
826 fs::is_directory(lyxrc.document_path))
827 package().document_dir() = lyxrc.document_path;
829 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
830 if (package().temp_dir().empty()) {
831 Alert::error(_("Could not create temporary directory"),
832 bformat(_("Could not create a temporary directory in\n"
833 "%1$s. Make sure that this\n"
834 "path exists and is writable and try again."),
835 from_utf8(lyxrc.tempdir_path)));
836 // createLyXTmpDir() tries sufficiently hard to create a
837 // usable temp dir, so the probability to come here is
838 // close to zero. We therefore don't try to overcome this
839 // problem with e.g. asking the user for a new path and
840 // trying again but simply exit.
844 if (lyxerr.debugging(Debug::INIT)) {
845 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
848 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
849 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
851 // This must happen after package initialization and after lyxrc is
852 // read, therefore it can't be done by a static object.
853 ConverterCache::init();
859 void LyX::defaultKeyBindings(kb_keymap * kbmap)
861 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
862 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
863 kbmap->bind("Up", FuncRequest(LFUN_UP));
864 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
866 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
867 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
868 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
869 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
871 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
872 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
873 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
874 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
876 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
877 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
879 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
880 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
882 // kbmap->bindings to enable the use of the numeric keypad
884 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
885 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
886 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
887 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
888 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
889 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
890 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
891 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
892 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
893 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
894 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
895 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
896 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
897 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
898 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
899 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
900 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
901 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
902 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
903 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
904 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
905 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
906 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
907 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
911 void LyX::emergencyCleanup() const
913 // what to do about tmpfiles is non-obvious. we would
914 // like to delete any we find, but our lyxdir might
915 // contain documents etc. which might be helpful on
918 pimpl_->buffer_list_.emergencyWriteAll();
920 pimpl_->lyx_server_->emergencyCleanup();
921 pimpl_->lyx_server_.reset();
922 pimpl_->lyx_socket_.reset();
927 void LyX::deadKeyBindings(kb_keymap * kbmap)
929 // bindKeyings for transparent handling of deadkeys
930 // The keysyms are gotten from XFree86 X11R6
931 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
932 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
933 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
934 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
935 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
936 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
937 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
938 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
939 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
940 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
941 // nothing with this name
942 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
943 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
944 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
945 // nothing with this name either...
946 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
947 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
948 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
949 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
955 // return true if file does not exist or is older than configure.py.
956 bool needsUpdate(string const & file)
958 static string const configure_script =
959 addName(package().system_support(), "configure.py");
960 string const absfile =
961 addName(package().user_support(), file);
963 return (! fs::exists(absfile))
964 || (fs::last_write_time(configure_script)
965 > fs::last_write_time(absfile));
971 bool LyX::queryUserLyXDir(bool explicit_userdir)
973 // Does user directory exist?
974 if (fs::exists(package().user_support()) &&
975 fs::is_directory(package().user_support())) {
978 return needsUpdate("lyxrc.defaults")
979 || needsUpdate("textclass.lst")
980 || needsUpdate("packages.lst");
983 first_start = !explicit_userdir;
985 // If the user specified explicitly a directory, ask whether
986 // to create it. If the user says "no", then exit.
987 if (explicit_userdir &&
989 _("Missing user LyX directory"),
990 bformat(_("You have specified a non-existent user "
991 "LyX directory, %1$s.\n"
992 "It is needed to keep your own configuration."),
993 from_utf8(package().user_support())),
995 _("&Create directory"),
997 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
998 earlyExit(EXIT_FAILURE);
1001 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1002 from_utf8(package().user_support())))
1005 if (!createDirectory(package().user_support(), 0755)) {
1006 // Failed, so let's exit.
1007 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1009 earlyExit(EXIT_FAILURE);
1016 bool LyX::readRcFile(string const & name)
1018 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1020 FileName const lyxrc_path = libFileSearch(string(), name);
1021 if (!lyxrc_path.empty()) {
1023 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1025 if (lyxrc.read(lyxrc_path) < 0) {
1026 showFileError(name);
1030 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1036 // Read the ui file `name'
1037 bool LyX::readUIFile(string const & name)
1047 struct keyword_item uitags[ui_last - 1] = {
1048 { "include", ui_include },
1049 { "menuset", ui_menuset },
1050 { "toolbar", ui_toolbar },
1051 { "toolbars", ui_toolbars }
1054 // Ensure that a file is read only once (prevents include loops)
1055 static std::list<string> uifiles;
1056 std::list<string>::const_iterator it = uifiles.begin();
1057 std::list<string>::const_iterator end = uifiles.end();
1058 it = std::find(it, end, name);
1060 lyxerr[Debug::INIT] << "UI file '" << name
1061 << "' has been read already. "
1062 << "Is this an include loop?"
1067 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1069 FileName const ui_path = libFileSearch("ui", name, "ui");
1071 if (ui_path.empty()) {
1072 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1073 showFileError(name);
1076 uifiles.push_back(name);
1078 lyxerr[Debug::INIT] << "Found " << name
1079 << " in " << ui_path << endl;
1080 LyXLex lex(uitags, ui_last - 1);
1081 lex.setFile(ui_path);
1083 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1087 if (lyxerr.debugging(Debug::PARSER))
1088 lex.printTable(lyxerr);
1090 while (lex.isOK()) {
1091 switch (lex.lex()) {
1094 string const file = lex.getString();
1095 if (!readUIFile(file))
1100 menubackend.read(lex);
1104 toolbarbackend.read(lex);
1108 toolbarbackend.readToolbars(lex);
1112 if (!rtrim(lex.getString()).empty())
1113 lex.printError("LyX::ReadUIFile: "
1114 "Unknown menu tag: `$$Token'");
1122 // Read the languages file `name'
1123 bool LyX::readLanguagesFile(string const & name)
1125 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1127 FileName const lang_path = libFileSearch(string(), name);
1128 if (lang_path.empty()) {
1129 showFileError(name);
1132 languages.read(lang_path);
1137 // Read the encodings file `name'
1138 bool LyX::readEncodingsFile(string const & name)
1140 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1142 FileName const enc_path = libFileSearch(string(), name);
1143 if (enc_path.empty()) {
1144 showFileError(name);
1147 encodings.read(enc_path);
1156 /// return the the number of arguments consumed
1157 typedef boost::function<int(string const &, string const &)> cmd_helper;
1159 int parse_dbg(string const & arg, string const &)
1162 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1163 Debug::showTags(lyxerr);
1166 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1168 lyxerr.level(Debug::value(arg));
1169 Debug::showLevel(lyxerr, lyxerr.level());
1174 int parse_help(string const &, string const &)
1177 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1178 "Command line switches (case sensitive):\n"
1179 "\t-help summarize LyX usage\n"
1180 "\t-userdir dir set user directory to dir\n"
1181 "\t-sysdir dir set system directory to dir\n"
1182 "\t-geometry WxH+X+Y set geometry of the main window\n"
1183 "\t-dbg feature[,feature]...\n"
1184 " select the features to debug.\n"
1185 " Type `lyx -dbg' to see the list of features\n"
1186 "\t-x [--execute] command\n"
1187 " where command is a lyx command.\n"
1188 "\t-e [--export] fmt\n"
1189 " where fmt is the export format of choice.\n"
1190 "\t-i [--import] fmt file.xxx\n"
1191 " where fmt is the import format of choice\n"
1192 " and file.xxx is the file to be imported.\n"
1193 "\t-version summarize version and build info\n"
1194 "Check the LyX man page for more details.")) << endl;
1199 int parse_version(string const &, string const &)
1201 lyxerr << "LyX " << lyx_version
1202 << " (" << lyx_release_date << ")" << endl;
1203 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1205 lyxerr << lyx_version_info << endl;
1210 int parse_sysdir(string const & arg, string const &)
1213 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1216 cl_system_support = arg;
1220 int parse_userdir(string const & arg, string const &)
1223 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1226 cl_user_support = arg;
1230 int parse_execute(string const & arg, string const &)
1233 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1240 int parse_export(string const & type, string const &)
1243 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1244 "--export switch")) << endl;
1247 batch = "buffer-export " + type;
1252 int parse_import(string const & type, string const & file)
1255 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1256 "--import switch")) << endl;
1260 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1264 batch = "buffer-import " + type + ' ' + file;
1271 void LyX::easyParse(int & argc, char * argv[])
1273 std::map<string, cmd_helper> cmdmap;
1275 cmdmap["-dbg"] = parse_dbg;
1276 cmdmap["-help"] = parse_help;
1277 cmdmap["--help"] = parse_help;
1278 cmdmap["-version"] = parse_version;
1279 cmdmap["--version"] = parse_version;
1280 cmdmap["-sysdir"] = parse_sysdir;
1281 cmdmap["-userdir"] = parse_userdir;
1282 cmdmap["-x"] = parse_execute;
1283 cmdmap["--execute"] = parse_execute;
1284 cmdmap["-e"] = parse_export;
1285 cmdmap["--export"] = parse_export;
1286 cmdmap["-i"] = parse_import;
1287 cmdmap["--import"] = parse_import;
1289 for (int i = 1; i < argc; ++i) {
1290 std::map<string, cmd_helper>::const_iterator it
1291 = cmdmap.find(argv[i]);
1293 // check for X11 -geometry option
1294 if (support::compare(argv[i], "-geometry") == 0)
1295 geometryOption_ = true;
1297 // don't complain if not found - may be parsed later
1298 if (it == cmdmap.end())
1301 string arg((i + 1 < argc) ? argv[i + 1] : "");
1302 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1304 int const remove = 1 + it->second(arg, arg2);
1306 // Now, remove used arguments by shifting
1307 // the following ones remove places down.
1309 for (int j = i; j < argc; ++j)
1310 argv[j] = argv[j + remove];
1314 batch_command = batch;
1318 FuncStatus getStatus(FuncRequest const & action)
1320 return LyX::ref().lyxFunc().getStatus(action);
1324 void dispatch(FuncRequest const & action)
1326 LyX::ref().lyxFunc().dispatch(action);
1330 BufferList & theBufferList()
1332 return LyX::ref().bufferList();
1336 LyXFunc & theLyXFunc()
1338 return LyX::ref().lyxFunc();
1342 LyXServer & theLyXServer()
1344 // FIXME: this should not be use_gui dependent
1345 BOOST_ASSERT(use_gui);
1346 return LyX::ref().server();
1350 LyXServerSocket & theLyXServerSocket()
1352 // FIXME: this should not be use_gui dependent
1353 BOOST_ASSERT(use_gui);
1354 return LyX::ref().socket();
1358 kb_keymap & theTopLevelKeymap()
1360 BOOST_ASSERT(use_gui);
1361 return LyX::ref().topLevelKeymap();
1365 IconvProcessor & utf8ToUcs4()
1367 return LyX::ref().iconvProcessor();