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(argv[0], cl_system_support, cl_user_support,
336 support::top_build_dir_is_one_level_up);
339 // FIXME: create a ConsoleApplication
340 int exit_status = init(argc, argv);
348 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
353 BufferList::iterator begin = pimpl_->buffer_list_.begin();
354 BufferList::iterator end = pimpl_->buffer_list_.end();
356 bool final_success = false;
357 for (BufferList::iterator I = begin; I != end; ++I) {
359 bool success = false;
360 buf->dispatch(batch_command, &success);
361 final_success |= success;
364 return !final_success;
367 // Force adding of font path _before_ Application is initialized
368 support::addFontResources();
370 // Let the frontend parse and remove all arguments that it knows
371 pimpl_->application_.reset(createApplication(argc, argv));
376 /* Create a CoreApplication class that will provide the main event loop
377 * and the socket callback registering. With Qt4, only QtCore
378 * library would be needed.
379 * When this is done, a server_mode could be created and the following two
380 * line would be moved out from here.
382 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
383 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
384 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
386 // Parse and remove all known arguments in the LyX singleton
387 // Give an error for all remaining ones.
388 int exit_status = init(argc, argv);
390 // Kill the application object before exiting.
391 pimpl_->application_.reset();
397 // Start the real execution loop.
398 exit_status = pimpl_->application_->exec();
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(FileName(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);
437 if (pimpl_->session_)
438 pimpl_->session_->writeFile();
439 pimpl_->session_.reset();
440 pimpl_->lyx_server_.reset();
441 pimpl_->lyx_socket_.reset();
444 // Kill the application object before exiting. This avoids crashes
445 // when exiting on Linux.
446 if (pimpl_->application_)
447 pimpl_->application_.reset();
451 void LyX::earlyExit(int status)
453 BOOST_ASSERT(pimpl_->application_.get());
454 // LyX::pimpl_::application_ is not initialised at this
455 // point so it's safe to just exit after some cleanup.
461 int LyX::init(int & argc, char * argv[])
463 // check for any spurious extra arguments
464 // other than documents
465 for (int argi = 1; argi < argc ; ++argi) {
466 if (argv[argi][0] == '-') {
468 bformat(_("Wrong command line option `%1$s'. Exiting."),
469 from_utf8(argv[argi]))) << endl;
474 // Initialization of LyX (reads lyxrc and more)
475 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
476 bool success = init();
477 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
481 for (int argi = argc - 1; argi >= 1; --argi) {
482 // check for any remaining extra arguments other than
483 // document file names. These will be passed out to the
485 if (argv[argi][0] == '-')
487 // get absolute path of file and add ".lyx" to
488 // the filename if necessary
489 pimpl_->files_to_load_.push_back(fileSearch(string(), os::internal_path(argv[argi]), "lyx"));
493 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
499 void LyX::loadFiles()
501 vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
502 vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
504 for (; it != end; ++it) {
508 Buffer * const b = newFile(it->absFilename(), string(), true);
509 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
510 if (loadLyXFile(buf, *it)) {
511 ErrorList const & el = buf->errorList("Parse");
513 for_each(el.begin(), el.end(),
514 boost::bind(&LyX::printError, this, _1));
517 pimpl_->buffer_list_.release(buf);
522 void LyX::execBatchCommands()
524 // The advantage of doing this here is that the event loop
525 // is already started. So any need for interaction will be
529 // Execute batch commands if available
530 if (batch_command.empty())
533 lyxerr[Debug::INIT] << "About to handle -x '"
534 << batch_command << '\'' << endl;
536 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
540 void LyX::restoreGuiSession()
542 LyXView * view = newLyXView();
544 // if some files were specified at command-line we assume that the
545 // user wants to edit *these* files and not to restore the session.
546 if (!pimpl_->files_to_load_.empty()) {
547 for_each(pimpl_->files_to_load_.begin(),
548 pimpl_->files_to_load_.end(),
549 bind(&LyXView::loadLyXFile, view, _1, true));
550 // clear this list to save a few bytes of RAM
551 pimpl_->files_to_load_.clear();
552 pimpl_->session_->lastOpened().clear();
556 if (!lyxrc.load_session)
559 vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
560 // do not add to the lastfile list since these files are restored from
561 // last session, and should be already there (regular files), or should
562 // not be added at all (help files).
563 for_each(lastopened.begin(), lastopened.end(),
564 bind(&LyXView::loadLyXFile, view, _1, false));
566 // clear this list to save a few bytes of RAM
567 pimpl_->session_->lastOpened().clear();
571 LyXView * LyX::newLyXView()
576 // determine windows size and position, from lyxrc and/or session
578 unsigned int width = 690;
579 unsigned int height = 510;
580 // default icon size, will be overwritten by stored session value
581 unsigned int iconSizeXY = 0;
582 bool maximize = false;
584 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
585 width = lyxrc.geometry_width;
586 height = lyxrc.geometry_height;
588 // if lyxrc returns (0,0), then use session info
590 string val = session().sessionInfo().load("WindowWidth");
592 width = convert<unsigned int>(val);
593 val = session().sessionInfo().load("WindowHeight");
595 height = convert<unsigned int>(val);
596 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
598 val = session().sessionInfo().load("IconSizeXY");
600 iconSizeXY = convert<unsigned int>(val);
603 // if user wants to restore window position
606 if (lyxrc.geometry_xysaved) {
607 string val = session().sessionInfo().load("WindowPosX");
609 posx = convert<int>(val);
610 val = session().sessionInfo().load("WindowPosY");
612 posy = convert<int>(val);
615 if (!geometryArg.empty())
621 // create the main window
622 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
630 The SIGHUP signal does not exist on Windows and does not need to be handled.
632 Windows handles SIGFPE and SIGSEGV signals as expected.
634 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
635 cause a new thread to be spawned. This may well result in unexpected
636 behaviour by the single-threaded LyX.
638 SIGTERM signals will come only from another process actually sending
639 that signal using 'raise' in Windows' POSIX compatability layer. It will
640 not come from the general "terminate process" methods that everyone
641 actually uses (and which can't be trapped). Killing an app 'politely' on
642 Windows involves first sending a WM_CLOSE message, something that is
643 caught already by the Qt frontend.
645 For more information see:
647 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
648 ...signals are mostly useless on Windows for a variety of reasons that are
651 'UNIX Application Migration Guide, Chapter 9'
652 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
654 'How To Terminate an Application "Cleanly" in Win32'
655 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
659 static void error_handler(int err_sig)
661 // Throw away any signals other than the first one received.
662 static sig_atomic_t handling_error = false;
665 handling_error = true;
667 // We have received a signal indicating a fatal error, so
668 // try and save the data ASAP.
669 LyX::cref().emergencyCleanup();
671 // These lyxerr calls may or may not work:
673 // Signals are asynchronous, so the main program may be in a very
674 // fragile state when a signal is processed and thus while a signal
675 // handler function executes.
676 // In general, therefore, we should avoid performing any
677 // I/O operations or calling most library and system functions from
680 // This shouldn't matter here, however, as we've already invoked
685 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
689 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
692 lyxerr << "\nlyx: SIGSEGV signal caught\n"
693 "Sorry, you have found a bug in LyX. "
694 "Please read the bug-reporting instructions "
695 "in Help->Introduction and send us a bug report, "
696 "if necessary. Thanks !\nBye." << endl;
704 // Deinstall the signal handlers
706 signal(SIGHUP, SIG_DFL);
708 signal(SIGINT, SIG_DFL);
709 signal(SIGFPE, SIG_DFL);
710 signal(SIGSEGV, SIG_DFL);
711 signal(SIGTERM, SIG_DFL);
714 if (err_sig == SIGSEGV ||
715 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
717 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
726 void LyX::printError(ErrorItem const & ei)
728 docstring tmp = _("LyX: ") + ei.error + char_type(':')
730 std::cerr << to_utf8(tmp) << std::endl;
734 void LyX::initGuiFont()
736 if (lyxrc.roman_font_name.empty())
737 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
739 if (lyxrc.sans_font_name.empty())
740 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
742 if (lyxrc.typewriter_font_name.empty())
743 lyxrc.typewriter_font_name
744 = pimpl_->application_->typewriterFontName();
751 signal(SIGHUP, error_handler);
753 signal(SIGFPE, error_handler);
754 signal(SIGSEGV, error_handler);
755 signal(SIGINT, error_handler);
756 signal(SIGTERM, error_handler);
757 // SIGPIPE can be safely ignored.
759 lyxrc.tempdir_path = package().temp_dir();
760 lyxrc.document_path = package().document_dir();
762 if (lyxrc.template_path.empty()) {
763 lyxrc.template_path = addPath(package().system_support(),
768 // Read configuration files
771 // This one may have been distributed along with LyX.
772 if (!readRcFile("lyxrc.dist"))
775 // Set the PATH correctly.
776 #if !defined (USE_POSIX_PACKAGING)
777 // Add the directory containing the LyX executable to the path
778 // so that LyX can find things like tex2lyx.
779 if (package().build_support().empty())
780 prependEnvPath("PATH", package().binary_dir());
782 if (!lyxrc.path_prefix.empty())
783 prependEnvPath("PATH", lyxrc.path_prefix);
785 // Check that user LyX directory is ok.
786 if (queryUserLyXDir(package().explicit_user_support()))
787 reconfigureUserLyXDir();
789 // no need for a splash when there is no GUI
794 // This one is generated in user_support directory by lib/configure.py.
795 if (!readRcFile("lyxrc.defaults"))
798 // Query the OS to know what formats are viewed natively
799 formats.setAutoOpen();
801 // Read lyxrc.dist again to be able to override viewer auto-detection.
802 readRcFile("lyxrc.dist");
804 system_lyxrc = lyxrc;
805 system_formats = formats;
806 system_converters = converters;
807 system_movers = movers;
808 system_lcolor = lcolor;
810 // This one is edited through the preferences dialog.
811 if (!readRcFile("preferences"))
814 if (!readEncodingsFile("encodings"))
816 if (!readLanguagesFile("languages"))
820 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
826 pimpl_->toplevel_keymap_.reset(new kb_keymap);
827 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
828 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
830 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
833 if (!readUIFile(lyxrc.ui_file))
837 if (lyxerr.debugging(Debug::LYXRC))
840 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
841 if (!lyxrc.path_prefix.empty())
842 prependEnvPath("PATH", lyxrc.path_prefix);
844 FileName const document_path(lyxrc.document_path);
845 if (fs::exists(document_path.toFilesystemEncoding()) &&
846 fs::is_directory(document_path.toFilesystemEncoding()))
847 package().document_dir() = lyxrc.document_path;
849 package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
850 if (package().temp_dir().empty()) {
851 Alert::error(_("Could not create temporary directory"),
852 bformat(_("Could not create a temporary directory in\n"
853 "%1$s. Make sure that this\n"
854 "path exists and is writable and try again."),
855 from_utf8(lyxrc.tempdir_path)));
856 // createLyXTmpDir() tries sufficiently hard to create a
857 // usable temp dir, so the probability to come here is
858 // close to zero. We therefore don't try to overcome this
859 // problem with e.g. asking the user for a new path and
860 // trying again but simply exit.
864 if (lyxerr.debugging(Debug::INIT)) {
865 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
868 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
869 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
871 // This must happen after package initialization and after lyxrc is
872 // read, therefore it can't be done by a static object.
873 ConverterCache::init();
879 void LyX::defaultKeyBindings(kb_keymap * kbmap)
881 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
882 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
883 kbmap->bind("Up", FuncRequest(LFUN_UP));
884 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
886 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
887 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
888 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
889 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
891 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
892 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
893 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
894 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
896 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
897 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
899 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
900 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
902 // kbmap->bindings to enable the use of the numeric keypad
904 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
905 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
906 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
907 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
908 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
909 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
910 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
911 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
912 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
913 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
914 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
915 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
916 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
917 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
918 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
919 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
920 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
921 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
922 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
923 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
924 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
925 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
926 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
927 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
931 void LyX::emergencyCleanup() const
933 // what to do about tmpfiles is non-obvious. we would
934 // like to delete any we find, but our lyxdir might
935 // contain documents etc. which might be helpful on
938 pimpl_->buffer_list_.emergencyWriteAll();
940 if (pimpl_->lyx_server_)
941 pimpl_->lyx_server_->emergencyCleanup();
942 pimpl_->lyx_server_.reset();
943 pimpl_->lyx_socket_.reset();
948 void LyX::deadKeyBindings(kb_keymap * kbmap)
950 // bindKeyings for transparent handling of deadkeys
951 // The keysyms are gotten from XFree86 X11R6
952 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
953 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
954 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
955 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
956 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
957 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
958 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
959 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
960 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
961 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
962 // nothing with this name
963 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
964 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
965 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
966 // nothing with this name either...
967 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
968 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
969 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
970 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
976 // return true if file does not exist or is older than configure.py.
977 bool needsUpdate(string const & file)
979 // We cannot initialize configure_script directly because the package
980 // is not initialized yet when static objects are constructed.
981 static string configure_script;
982 static bool firstrun = true;
984 configure_script = FileName(addName(
985 package().system_support(),
986 "configure.py")).toFilesystemEncoding();
990 string const absfile = FileName(addName(
991 package().user_support(), file)).toFilesystemEncoding();
992 return (! fs::exists(absfile))
993 || (fs::last_write_time(configure_script)
994 > fs::last_write_time(absfile));
1000 bool LyX::queryUserLyXDir(bool explicit_userdir)
1002 // Does user directory exist?
1003 string const user_support =
1004 FileName(package().user_support()).toFilesystemEncoding();
1005 if (fs::exists(user_support) && fs::is_directory(user_support)) {
1006 first_start = false;
1008 return needsUpdate("lyxrc.defaults")
1009 || needsUpdate("textclass.lst")
1010 || needsUpdate("packages.lst");
1013 first_start = !explicit_userdir;
1015 // If the user specified explicitly a directory, ask whether
1016 // to create it. If the user says "no", then exit.
1017 if (explicit_userdir &&
1019 _("Missing user LyX directory"),
1020 bformat(_("You have specified a non-existent user "
1021 "LyX directory, %1$s.\n"
1022 "It is needed to keep your own configuration."),
1023 from_utf8(package().user_support())),
1025 _("&Create directory"),
1027 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1028 earlyExit(EXIT_FAILURE);
1031 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1032 from_utf8(package().user_support())))
1035 if (!createDirectory(package().user_support(), 0755)) {
1036 // Failed, so let's exit.
1037 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1039 earlyExit(EXIT_FAILURE);
1046 bool LyX::readRcFile(string const & name)
1048 lyxerr[Debug::INIT] << "About to read " << name << "... ";
1050 FileName const lyxrc_path = libFileSearch(string(), name);
1051 if (!lyxrc_path.empty()) {
1053 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1055 if (lyxrc.read(lyxrc_path) < 0) {
1056 showFileError(name);
1060 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1066 // Read the ui file `name'
1067 bool LyX::readUIFile(string const & name)
1077 struct keyword_item uitags[ui_last - 1] = {
1078 { "include", ui_include },
1079 { "menuset", ui_menuset },
1080 { "toolbar", ui_toolbar },
1081 { "toolbars", ui_toolbars }
1084 // Ensure that a file is read only once (prevents include loops)
1085 static std::list<string> uifiles;
1086 std::list<string>::const_iterator it = uifiles.begin();
1087 std::list<string>::const_iterator end = uifiles.end();
1088 it = std::find(it, end, name);
1090 lyxerr[Debug::INIT] << "UI file '" << name
1091 << "' has been read already. "
1092 << "Is this an include loop?"
1097 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1099 FileName const ui_path = libFileSearch("ui", name, "ui");
1101 if (ui_path.empty()) {
1102 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1103 showFileError(name);
1106 uifiles.push_back(name);
1108 lyxerr[Debug::INIT] << "Found " << name
1109 << " in " << ui_path << endl;
1110 LyXLex lex(uitags, ui_last - 1);
1111 lex.setFile(ui_path);
1113 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1117 if (lyxerr.debugging(Debug::PARSER))
1118 lex.printTable(lyxerr);
1120 while (lex.isOK()) {
1121 switch (lex.lex()) {
1124 string const file = lex.getString();
1125 if (!readUIFile(file))
1130 menubackend.read(lex);
1134 toolbarbackend.read(lex);
1138 toolbarbackend.readToolbars(lex);
1142 if (!rtrim(lex.getString()).empty())
1143 lex.printError("LyX::ReadUIFile: "
1144 "Unknown menu tag: `$$Token'");
1152 // Read the languages file `name'
1153 bool LyX::readLanguagesFile(string const & name)
1155 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1157 FileName const lang_path = libFileSearch(string(), name);
1158 if (lang_path.empty()) {
1159 showFileError(name);
1162 languages.read(lang_path);
1167 // Read the encodings file `name'
1168 bool LyX::readEncodingsFile(string const & name)
1170 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1172 FileName const enc_path = libFileSearch(string(), name);
1173 if (enc_path.empty()) {
1174 showFileError(name);
1177 encodings.read(enc_path);
1186 /// return the the number of arguments consumed
1187 typedef boost::function<int(string const &, string const &)> cmd_helper;
1189 int parse_dbg(string const & arg, string const &)
1192 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1193 Debug::showTags(lyxerr);
1196 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1198 lyxerr.level(Debug::value(arg));
1199 Debug::showLevel(lyxerr, lyxerr.level());
1204 int parse_help(string const &, string const &)
1207 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1208 "Command line switches (case sensitive):\n"
1209 "\t-help summarize LyX usage\n"
1210 "\t-userdir dir set user directory to dir\n"
1211 "\t-sysdir dir set system directory to dir\n"
1212 "\t-geometry WxH+X+Y set geometry of the main window\n"
1213 "\t-dbg feature[,feature]...\n"
1214 " select the features to debug.\n"
1215 " Type `lyx -dbg' to see the list of features\n"
1216 "\t-x [--execute] command\n"
1217 " where command is a lyx command.\n"
1218 "\t-e [--export] fmt\n"
1219 " where fmt is the export format of choice.\n"
1220 "\t-i [--import] fmt file.xxx\n"
1221 " where fmt is the import format of choice\n"
1222 " and file.xxx is the file to be imported.\n"
1223 "\t-version summarize version and build info\n"
1224 "Check the LyX man page for more details.")) << endl;
1229 int parse_version(string const &, string const &)
1231 lyxerr << "LyX " << lyx_version
1232 << " (" << lyx_release_date << ")" << endl;
1233 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1235 lyxerr << lyx_version_info << endl;
1240 int parse_sysdir(string const & arg, string const &)
1243 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1246 cl_system_support = arg;
1250 int parse_userdir(string const & arg, string const &)
1253 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1256 cl_user_support = arg;
1260 int parse_execute(string const & arg, string const &)
1263 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1270 int parse_export(string const & type, string const &)
1273 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1274 "--export switch")) << endl;
1277 batch = "buffer-export " + type;
1282 int parse_import(string const & type, string const & file)
1285 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1286 "--import switch")) << endl;
1290 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1294 batch = "buffer-import " + type + ' ' + file;
1298 int parse_geometry(string const & arg1, string const &)
1301 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1302 // remove also the arg
1305 // don't remove "-geometry"
1314 void LyX::easyParse(int & argc, char * argv[])
1316 std::map<string, cmd_helper> cmdmap;
1318 cmdmap["-dbg"] = parse_dbg;
1319 cmdmap["-help"] = parse_help;
1320 cmdmap["--help"] = parse_help;
1321 cmdmap["-version"] = parse_version;
1322 cmdmap["--version"] = parse_version;
1323 cmdmap["-sysdir"] = parse_sysdir;
1324 cmdmap["-userdir"] = parse_userdir;
1325 cmdmap["-x"] = parse_execute;
1326 cmdmap["--execute"] = parse_execute;
1327 cmdmap["-e"] = parse_export;
1328 cmdmap["--export"] = parse_export;
1329 cmdmap["-i"] = parse_import;
1330 cmdmap["--import"] = parse_import;
1331 cmdmap["-geometry"] = parse_geometry;
1333 for (int i = 1; i < argc; ++i) {
1334 std::map<string, cmd_helper>::const_iterator it
1335 = cmdmap.find(argv[i]);
1337 // don't complain if not found - may be parsed later
1338 if (it == cmdmap.end())
1341 string arg((i + 1 < argc) ? argv[i + 1] : "");
1342 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1344 int const remove = 1 + it->second(arg, arg2);
1346 // Now, remove used arguments by shifting
1347 // the following ones remove places down.
1350 for (int j = i; j < argc; ++j)
1351 argv[j] = argv[j + remove];
1356 batch_command = batch;
1360 FuncStatus getStatus(FuncRequest const & action)
1362 return LyX::ref().lyxFunc().getStatus(action);
1366 void dispatch(FuncRequest const & action)
1368 LyX::ref().lyxFunc().dispatch(action);
1372 BufferList & theBufferList()
1374 return LyX::ref().bufferList();
1378 LyXFunc & theLyXFunc()
1380 return LyX::ref().lyxFunc();
1384 LyXServer & theLyXServer()
1386 // FIXME: this should not be use_gui dependent
1387 BOOST_ASSERT(use_gui);
1388 return LyX::ref().server();
1392 LyXServerSocket & theLyXServerSocket()
1394 // FIXME: this should not be use_gui dependent
1395 BOOST_ASSERT(use_gui);
1396 return LyX::ref().socket();
1400 kb_keymap & theTopLevelKeymap()
1402 BOOST_ASSERT(use_gui);
1403 return LyX::ref().topLevelKeymap();
1407 IconvProcessor & utf8ToUcs4()
1409 return LyX::ref().iconvProcessor();