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.
21 #include "buffer_funcs.h"
22 #include "bufferlist.h"
23 #include "converter.h"
26 #include "errorlist.h"
34 #include "LyXAction.h"
38 #include "lyxserver.h"
39 #include "lyxsocket.h"
40 #include "lyxtextclasslist.h"
41 #include "MenuBackend.h"
43 #include "ToolbarBackend.h"
45 #include "frontends/Alert.h"
46 #include "frontends/Application.h"
47 #include "frontends/Gui.h"
48 #include "frontends/LyXView.h"
50 #include "support/environment.h"
51 #include "support/filetools.h"
52 #include "support/fontutils.h"
53 #include "support/lyxlib.h"
54 #include "support/convert.h"
55 #include "support/os.h"
56 #include "support/package.h"
57 #include "support/path.h"
58 #include "support/systemcall.h"
60 #include <boost/bind.hpp>
61 #include <boost/filesystem/operations.hpp>
69 using support::addName;
70 using support::addPath;
71 using support::bformat;
72 using support::createDirectory;
73 using support::createLyXTmpDir;
74 using support::destroyDir;
75 using support::fileSearch;
76 using support::getEnv;
77 using support::i18nLibFileSearch;
78 using support::libFileSearch;
79 using support::package;
80 using support::prependEnvPath;
82 using support::Systemcall;
84 namespace Alert = frontend::Alert;
85 namespace os = support::os;
86 namespace fs = boost::filesystem;
93 #ifndef CXX_GLOBAL_CSTD
100 frontend::Application * theApp = 0;
102 /// are we using the GUI at all?
104 * We default to true and this is changed to false when the export feature is used.
111 // Filled with the command line arguments "foo" of "-sysdir foo" or
113 string cl_system_support;
114 string cl_user_support;
117 void showFileError(string const & error)
119 Alert::warning(_("Could not read configuration file"),
120 bformat(_("Error while reading the configuration file\n%1$s.\n"
121 "Please check your installation."), from_utf8(error)));
125 void reconfigureUserLyXDir()
127 string const configure_command = package().configure_command();
129 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
130 support::Path p(package().user_support());
132 one.startscript(Systemcall::Wait, configure_command);
133 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
139 /// The main application class private implementation.
140 struct LyX::Singletons
142 /// our function handler
145 BufferList buffer_list_;
147 boost::scoped_ptr<kb_keymap> toplevel_keymap_;
149 boost::scoped_ptr<LyXServer> lyx_server_;
151 boost::scoped_ptr<LyXServerSocket> lyx_socket_;
153 boost::scoped_ptr<frontend::Application> application_;
154 /// lyx session, containing lastfiles, lastfilepos, and lastopened
155 boost::scoped_ptr<Session> session_;
159 boost::scoped_ptr<LyX> LyX::singleton_;
162 int LyX::exec(int & argc, char * argv[])
164 BOOST_ASSERT(!singleton_.get());
165 // We must return from this before launching the gui so that
166 // other parts of the code can access singleton_ through
167 // LyX::ref and LyX::cref.
168 singleton_.reset(new LyX);
169 // Start the real execution loop.
170 return singleton_->priv_exec(argc, argv);
176 BOOST_ASSERT(singleton_.get());
177 return *singleton_.get();
181 LyX const & LyX::cref()
183 BOOST_ASSERT(singleton_.get());
184 return *singleton_.get();
189 : first_start(false), geometryOption_(false)
191 pimpl_.reset(new Singletons);
195 BufferList & LyX::bufferList()
197 return pimpl_->buffer_list_;
201 BufferList const & LyX::bufferList() const
203 return pimpl_->buffer_list_;
207 Session & LyX::session()
209 BOOST_ASSERT(pimpl_->session_.get());
210 return *pimpl_->session_.get();
214 Session const & LyX::session() const
216 BOOST_ASSERT(pimpl_->session_.get());
217 return *pimpl_->session_.get();
221 LyXFunc & LyX::lyxFunc()
223 return pimpl_->lyxfunc_;
227 LyXFunc const & LyX::lyxFunc() const
229 return pimpl_->lyxfunc_;
233 LyXServer & LyX::server()
235 BOOST_ASSERT(pimpl_->lyx_server_.get());
236 return *pimpl_->lyx_server_.get();
240 LyXServer const & LyX::server() const
242 BOOST_ASSERT(pimpl_->lyx_server_.get());
243 return *pimpl_->lyx_server_.get();
247 LyXServerSocket & LyX::socket()
249 BOOST_ASSERT(pimpl_->lyx_socket_.get());
250 return *pimpl_->lyx_socket_.get();
254 LyXServerSocket const & LyX::socket() const
256 BOOST_ASSERT(pimpl_->lyx_socket_.get());
257 return *pimpl_->lyx_socket_.get();
261 frontend::Application & LyX::application()
263 BOOST_ASSERT(pimpl_->application_.get());
264 return *pimpl_->application_.get();
268 frontend::Application const & LyX::application() const
270 BOOST_ASSERT(pimpl_->application_.get());
271 return *pimpl_->application_.get();
275 kb_keymap & LyX::topLevelKeymap()
277 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
278 return *pimpl_->toplevel_keymap_.get();
282 kb_keymap const & LyX::topLevelKeymap() const
284 BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
285 return *pimpl_->toplevel_keymap_.get();
289 Buffer const * const LyX::updateInset(InsetBase const * inset) const
294 Buffer const * buffer_ptr = 0;
295 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
296 vector<int>::const_iterator it = view_ids.begin();
297 vector<int>::const_iterator const end = view_ids.end();
298 for (; it != end; ++it) {
300 pimpl_->application_->gui().view(*it).updateInset(inset);
308 int LyX::priv_exec(int & argc, char * argv[])
310 // Here we need to parse the command line. At least
311 // we need to parse for "-dbg" and "-help"
312 easyParse(argc, argv);
314 support::init_package(argv[0], cl_system_support, cl_user_support,
315 support::top_build_dir_is_one_level_up);
317 vector<string> files;
318 int exit_status = execBatchCommands(argc, argv, files);
324 // Force adding of font path _before_ Application is initialized
325 support::addFontResources();
326 pimpl_->application_.reset(createApplication(argc, argv));
328 // FIXME: this global pointer should probably go.
329 theApp = pimpl_->application_.get();
330 restoreGuiSession(files);
331 // Start the real execution loop.
334 /* Create a CoreApplication class that will provide the main event loop
335 * and the socket callback registering. With Qt4, only QtCore
336 * library would be needed.
337 * When this is done, a server_mode could be created and the following two
338 * line would be moved out from here.
340 pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
341 pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
342 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
344 // handle the batch commands the user asked for
345 if (!batch_command.empty()) {
346 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
349 exit_status = pimpl_->application_->start(batch_command);
350 // Kill the application object before exiting. This avoid crash
352 pimpl_->application_.reset();
353 // Restore original font resources after Application is destroyed.
354 support::restoreFontResources();
357 // FIXME: create a ConsoleApplication
365 void LyX::prepareExit()
367 // Set a flag that we do quitting from the program,
368 // so no refreshes are necessary.
371 // close buffers first
372 pimpl_->buffer_list_.closeAll();
374 // do any other cleanup procedures now
375 lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
377 if (!destroyDir(package().temp_dir())) {
378 docstring const msg =
379 bformat(_("Unable to remove the temporary directory %1$s"),
380 from_utf8(package().temp_dir()));
381 Alert::warning(_("Unable to remove temporary directory"), msg);
386 void LyX::earlyExit(int status)
388 BOOST_ASSERT(pimpl_->application_.get());
389 // LyX::pimpl_::application_ is not initialised at this
390 // point so it's safe to just exit after some cleanup.
396 void LyX::quit(bool noask)
398 lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
401 if (!noask && !pimpl_->buffer_list_.quitWriteAll())
404 // The LyXView Geometry settings are stored when LyXView::close
405 // is called explicitely but a straight quit() command would not
406 // guarante that. So we make sure this is done here:
407 vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
408 for (size_t i = 0; i < view_ids.size(); ++i)
409 pimpl_->application_->gui().view(view_ids[i]).saveGeometry();
411 pimpl_->session_->writeFile();
417 pimpl_->lyx_server_.reset();
418 pimpl_->lyx_socket_.reset();
419 pimpl_->application_->exit(0);
424 int LyX::execBatchCommands(int & argc, char * argv[],
425 vector<string> & files)
427 // check for any spurious extra arguments
428 // other than documents
429 for (int argi = 1; argi < argc ; ++argi) {
430 if (argv[argi][0] == '-') {
432 bformat(_("Wrong command line option `%1$s'. Exiting."),
433 from_utf8(argv[argi]))) << endl;
438 // Initialization of LyX (reads lyxrc and more)
439 lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
440 bool success = init();
441 lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
445 for (int argi = argc - 1; argi >= 1; --argi)
446 files.push_back(os::internal_path(argv[argi]));
449 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
451 // Execute batch commands if available
452 if (!batch_command.empty()) {
454 lyxerr[Debug::INIT] << "About to handle -x '"
455 << batch_command << '\'' << endl;
457 Buffer * last_loaded = 0;
459 vector<string>::const_iterator it = files.begin();
460 vector<string>::const_iterator end = files.end();
462 for (; it != end; ++it) {
463 // get absolute path of file and add ".lyx" to
464 // the filename if necessary
465 string s = fileSearch(string(), *it, "lyx");
467 Buffer * const b = newFile(*it, string(), true);
471 Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
472 if (loadLyXFile(buf, s)) {
474 ErrorList const & el = buf->errorList("Parse");
476 for_each(el.begin(), el.end(),
477 boost::bind(&LyX::printError, this, _1));
480 pimpl_->buffer_list_.release(buf);
484 // try to dispatch to last loaded buffer first
487 if (last_loaded->dispatch(batch_command, &success)) {
492 files.clear(); // the files are already loaded
499 void LyX::restoreGuiSession(vector<string> const & files)
501 LyXView * view = newLyXView();
504 for_each(files.begin(), files.end(),
505 bind(&LyXView::loadLyXFile, view, _1, true));
507 // if a file is specified, I assume that user wants to edit *that* file
508 if (files.empty() && lyxrc.load_session) {
509 vector<string> const & lastopened = pimpl_->session_->lastOpenedFiles();
510 // do not add to the lastfile list since these files are restored from
511 // last seesion, and should be already there (regular files), or should
512 // not be added at all (help files).
513 for_each(lastopened.begin(), lastopened.end(),
514 bind(&LyXView::loadLyXFile, view, _1, false));
516 // clear this list to save a few bytes of RAM
517 pimpl_->session_->clearLastOpenedFiles();
521 LyXView * LyX::newLyXView()
526 // determine windows size and position, from lyxrc and/or session
528 unsigned int width = 690;
529 unsigned int height = 510;
530 bool maximize = false;
532 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
533 width = lyxrc.geometry_width;
534 height = lyxrc.geometry_height;
536 // if lyxrc returns (0,0), then use session info
538 string val = session().loadSessionInfo("WindowWidth");
540 width = convert<unsigned int>(val);
541 val = session().loadSessionInfo("WindowHeight");
543 height = convert<unsigned int>(val);
544 if (session().loadSessionInfo("WindowIsMaximized") == "yes")
548 // if user wants to restore window position
551 if (lyxrc.geometry_xysaved) {
552 string val = session().loadSessionInfo("WindowPosX");
554 posx = convert<int>(val);
555 val = session().loadSessionInfo("WindowPosY");
557 posy = convert<int>(val);
560 if (geometryOption_) {
564 // create the main window
565 LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
573 The SIGHUP signal does not exist on Windows and does not need to be handled.
575 Windows handles SIGFPE and SIGSEGV signals as expected.
577 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
578 cause a new thread to be spawned. This may well result in unexpected
579 behaviour by the single-threaded LyX.
581 SIGTERM signals will come only from another process actually sending
582 that signal using 'raise' in Windows' POSIX compatability layer. It will
583 not come from the general "terminate process" methods that everyone
584 actually uses (and which can't be trapped). Killing an app 'politely' on
585 Windows involves first sending a WM_CLOSE message, something that is
586 caught already by the Qt frontend.
588 For more information see:
590 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
591 ...signals are mostly useless on Windows for a variety of reasons that are
594 'UNIX Application Migration Guide, Chapter 9'
595 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
597 'How To Terminate an Application "Cleanly" in Win32'
598 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
602 static void error_handler(int err_sig)
604 // Throw away any signals other than the first one received.
605 static sig_atomic_t handling_error = false;
608 handling_error = true;
610 // We have received a signal indicating a fatal error, so
611 // try and save the data ASAP.
612 LyX::cref().emergencyCleanup();
614 // These lyxerr calls may or may not work:
616 // Signals are asynchronous, so the main program may be in a very
617 // fragile state when a signal is processed and thus while a signal
618 // handler function executes.
619 // In general, therefore, we should avoid performing any
620 // I/O operations or calling most library and system functions from
623 // This shouldn't matter here, however, as we've already invoked
628 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
632 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
635 lyxerr << "\nlyx: SIGSEGV signal caught\n"
636 "Sorry, you have found a bug in LyX. "
637 "Please read the bug-reporting instructions "
638 "in Help->Introduction and send us a bug report, "
639 "if necessary. Thanks !\nBye." << endl;
647 // Deinstall the signal handlers
649 signal(SIGHUP, SIG_DFL);
651 signal(SIGINT, SIG_DFL);
652 signal(SIGFPE, SIG_DFL);
653 signal(SIGSEGV, SIG_DFL);
654 signal(SIGTERM, SIG_DFL);
657 if (err_sig == SIGSEGV ||
658 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
660 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
669 void LyX::printError(ErrorItem const & ei)
671 docstring tmp = _("LyX: ") + ei.error + char_type(':')
673 std::cerr << to_utf8(tmp) << std::endl;
677 void LyX::initGuiFont()
679 if (lyxrc.roman_font_name.empty())
680 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
682 if (lyxrc.sans_font_name.empty())
683 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
685 if (lyxrc.typewriter_font_name.empty())
686 lyxrc.typewriter_font_name
687 = pimpl_->application_->typewriterFontName();
694 signal(SIGHUP, error_handler);
696 signal(SIGFPE, error_handler);
697 signal(SIGSEGV, error_handler);
698 signal(SIGINT, error_handler);
699 signal(SIGTERM, error_handler);
700 // SIGPIPE can be safely ignored.
702 lyxrc.tempdir_path = package().temp_dir();
703 lyxrc.document_path = package().document_dir();
705 if (lyxrc.template_path.empty()) {
706 lyxrc.template_path = addPath(package().system_support(),
711 // Read configuration files
714 // This one may have been distributed along with LyX.
715 if (!readRcFile("lyxrc.dist"))
718 // Set the PATH correctly.
719 #if !defined (USE_POSIX_PACKAGING)
720 // Add the directory containing the LyX executable to the path
721 // so that LyX can find things like tex2lyx.
722 if (package().build_support().empty())
723 prependEnvPath("PATH", package().binary_dir());
725 if (!lyxrc.path_prefix.empty())
726 prependEnvPath("PATH", lyxrc.path_prefix);
728 // Check that user LyX directory is ok.
729 if (queryUserLyXDir(package().explicit_user_support()))
730 reconfigureUserLyXDir();
732 // no need for a splash when there is no GUI
737 // This one is generated in user_support directory by lib/configure.py.
738 if (!readRcFile("lyxrc.defaults"))
741 // Query the OS to know what formats are viewed natively
742 formats.setAutoOpen();
744 system_lyxrc = lyxrc;
745 system_formats = formats;
746 system_converters = converters;
747 system_movers = movers;
748 system_lcolor = lcolor;
750 // This one is edited through the preferences dialog.
751 if (!readRcFile("preferences"))
754 if (!readEncodingsFile("encodings"))
756 if (!readLanguagesFile("languages"))
760 lyxerr[Debug::INIT] << "Reading layouts..." << endl;
766 pimpl_->toplevel_keymap_.reset(new kb_keymap);
767 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
768 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
770 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
773 if (!readUIFile(lyxrc.ui_file))
777 if (lyxerr.debugging(Debug::LYXRC))
780 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
781 if (!lyxrc.path_prefix.empty())
782 prependEnvPath("PATH", lyxrc.path_prefix);
784 if (fs::exists(lyxrc.document_path) &&
785 fs::is_directory(lyxrc.document_path))
786 package().document_dir() = lyxrc.document_path;
788 package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
789 if (package().temp_dir().empty()) {
790 Alert::error(_("Could not create temporary directory"),
791 bformat(_("Could not create a temporary directory in\n"
792 "%1$s. Make sure that this\n"
793 "path exists and is writable and try again."),
794 from_utf8(lyxrc.tempdir_path)));
795 // createLyXTmpDir() tries sufficiently hard to create a
796 // usable temp dir, so the probability to come here is
797 // close to zero. We therefore don't try to overcome this
798 // problem with e.g. asking the user for a new path and
799 // trying again but simply exit.
803 if (lyxerr.debugging(Debug::INIT)) {
804 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
807 lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
808 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
813 void LyX::defaultKeyBindings(kb_keymap * kbmap)
815 kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
816 kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
817 kbmap->bind("Up", FuncRequest(LFUN_UP));
818 kbmap->bind("Down", FuncRequest(LFUN_DOWN));
820 kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
821 kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
822 kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
823 kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
825 kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
826 kbmap->bind("End", FuncRequest(LFUN_LINE_END));
827 kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
828 kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
830 kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
831 //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
833 kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
834 kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
836 // kbmap->bindings to enable the use of the numeric keypad
838 //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
839 //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
840 kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
841 //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
842 //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
843 //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
844 //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
845 //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
846 //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
847 //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
848 //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
849 //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
850 //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
851 //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
852 //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
853 //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
854 kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
855 kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
856 kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
857 kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
858 kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
859 kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
860 kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
861 kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
865 void LyX::emergencyCleanup() const
867 // what to do about tmpfiles is non-obvious. we would
868 // like to delete any we find, but our lyxdir might
869 // contain documents etc. which might be helpful on
872 pimpl_->buffer_list_.emergencyWriteAll();
874 pimpl_->lyx_server_->emergencyCleanup();
875 pimpl_->lyx_server_.reset();
876 pimpl_->lyx_socket_.reset();
881 void LyX::deadKeyBindings(kb_keymap * kbmap)
883 // bindKeyings for transparent handling of deadkeys
884 // The keysyms are gotten from XFree86 X11R6
885 kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
886 kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
887 kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
888 kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
889 kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
890 kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
891 kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
892 kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
893 kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
894 kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
895 // nothing with this name
896 // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
897 kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
898 kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
899 // nothing with this name either...
900 //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
901 kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
902 kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
903 kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
909 // return true if file does not exist or is older than configure.py.
910 bool needsUpdate(string const & file)
912 static string const configure_script =
913 addName(package().system_support(), "configure.py");
914 string const absfile =
915 addName(package().user_support(), file);
917 return (! fs::exists(absfile))
918 || (fs::last_write_time(configure_script)
919 > fs::last_write_time(absfile));
925 bool LyX::queryUserLyXDir(bool explicit_userdir)
927 // Does user directory exist?
928 if (fs::exists(package().user_support()) &&
929 fs::is_directory(package().user_support())) {
932 return needsUpdate("lyxrc.defaults")
933 || needsUpdate("textclass.lst")
934 || needsUpdate("packages.lst");
937 first_start = !explicit_userdir;
939 // If the user specified explicitly a directory, ask whether
940 // to create it. If the user says "no", then exit.
941 if (explicit_userdir &&
943 _("Missing user LyX directory"),
944 bformat(_("You have specified a non-existent user "
945 "LyX directory, %1$s.\n"
946 "It is needed to keep your own configuration."),
947 from_utf8(package().user_support())),
949 _("&Create directory"),
951 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
952 earlyExit(EXIT_FAILURE);
955 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
956 from_utf8(package().user_support())))
959 if (!createDirectory(package().user_support(), 0755)) {
960 // Failed, so let's exit.
961 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
963 earlyExit(EXIT_FAILURE);
970 bool LyX::readRcFile(string const & name)
972 lyxerr[Debug::INIT] << "About to read " << name << "... ";
974 string const lyxrc_path = libFileSearch(string(), name);
975 if (!lyxrc_path.empty()) {
977 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
979 if (lyxrc.read(lyxrc_path) < 0) {
984 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
990 // Read the ui file `name'
991 bool LyX::readUIFile(string const & name)
1001 struct keyword_item uitags[ui_last - 1] = {
1002 { "include", ui_include },
1003 { "menuset", ui_menuset },
1004 { "toolbar", ui_toolbar },
1005 { "toolbars", ui_toolbars }
1008 // Ensure that a file is read only once (prevents include loops)
1009 static std::list<string> uifiles;
1010 std::list<string>::const_iterator it = uifiles.begin();
1011 std::list<string>::const_iterator end = uifiles.end();
1012 it = std::find(it, end, name);
1014 lyxerr[Debug::INIT] << "UI file '" << name
1015 << "' has been read already. "
1016 << "Is this an include loop?"
1021 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1023 string const ui_path = libFileSearch("ui", name, "ui");
1025 if (ui_path.empty()) {
1026 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1027 showFileError(name);
1030 uifiles.push_back(name);
1032 lyxerr[Debug::INIT] << "Found " << name
1033 << " in " << ui_path << endl;
1034 LyXLex lex(uitags, ui_last - 1);
1035 lex.setFile(ui_path);
1037 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1041 if (lyxerr.debugging(Debug::PARSER))
1042 lex.printTable(lyxerr);
1044 while (lex.isOK()) {
1045 switch (lex.lex()) {
1048 string const file = lex.getString();
1049 if (!readUIFile(file))
1054 menubackend.read(lex);
1058 toolbarbackend.read(lex);
1062 toolbarbackend.readToolbars(lex);
1066 if (!rtrim(lex.getString()).empty())
1067 lex.printError("LyX::ReadUIFile: "
1068 "Unknown menu tag: `$$Token'");
1076 // Read the languages file `name'
1077 bool LyX::readLanguagesFile(string const & name)
1079 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1081 string const lang_path = libFileSearch(string(), name);
1082 if (lang_path.empty()) {
1083 showFileError(name);
1086 languages.read(lang_path);
1091 // Read the encodings file `name'
1092 bool LyX::readEncodingsFile(string const & name)
1094 lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1096 string const enc_path = libFileSearch(string(), name);
1097 if (enc_path.empty()) {
1098 showFileError(name);
1101 encodings.read(enc_path);
1110 /// return the the number of arguments consumed
1111 typedef boost::function<int(string const &, string const &)> cmd_helper;
1113 int parse_dbg(string const & arg, string const &)
1116 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1117 Debug::showTags(lyxerr);
1120 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1122 lyxerr.level(Debug::value(arg));
1123 Debug::showLevel(lyxerr, lyxerr.level());
1128 int parse_help(string const &, string const &)
1131 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1132 "Command line switches (case sensitive):\n"
1133 "\t-help summarize LyX usage\n"
1134 "\t-userdir dir set user directory to dir\n"
1135 "\t-sysdir dir set system directory to dir\n"
1136 "\t-geometry WxH+X+Y set geometry of the main window\n"
1137 "\t-dbg feature[,feature]...\n"
1138 " select the features to debug.\n"
1139 " Type `lyx -dbg' to see the list of features\n"
1140 "\t-x [--execute] command\n"
1141 " where command is a lyx command.\n"
1142 "\t-e [--export] fmt\n"
1143 " where fmt is the export format of choice.\n"
1144 "\t-i [--import] fmt file.xxx\n"
1145 " where fmt is the import format of choice\n"
1146 " and file.xxx is the file to be imported.\n"
1147 "\t-version summarize version and build info\n"
1148 "Check the LyX man page for more details.")) << endl;
1153 int parse_version(string const &, string const &)
1155 lyxerr << "LyX " << lyx_version
1156 << " (" << lyx_release_date << ")" << endl;
1157 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1159 lyxerr << lyx_version_info << endl;
1164 int parse_sysdir(string const & arg, string const &)
1167 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1170 cl_system_support = arg;
1174 int parse_userdir(string const & arg, string const &)
1177 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1180 cl_user_support = arg;
1184 int parse_execute(string const & arg, string const &)
1187 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1194 int parse_export(string const & type, string const &)
1197 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1198 "--export switch")) << endl;
1201 batch = "buffer-export " + type;
1206 int parse_import(string const & type, string const & file)
1209 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1210 "--import switch")) << endl;
1214 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1218 batch = "buffer-import " + type + ' ' + file;
1225 void LyX::easyParse(int & argc, char * argv[])
1227 std::map<string, cmd_helper> cmdmap;
1229 cmdmap["-dbg"] = parse_dbg;
1230 cmdmap["-help"] = parse_help;
1231 cmdmap["--help"] = parse_help;
1232 cmdmap["-version"] = parse_version;
1233 cmdmap["--version"] = parse_version;
1234 cmdmap["-sysdir"] = parse_sysdir;
1235 cmdmap["-userdir"] = parse_userdir;
1236 cmdmap["-x"] = parse_execute;
1237 cmdmap["--execute"] = parse_execute;
1238 cmdmap["-e"] = parse_export;
1239 cmdmap["--export"] = parse_export;
1240 cmdmap["-i"] = parse_import;
1241 cmdmap["--import"] = parse_import;
1243 for (int i = 1; i < argc; ++i) {
1244 std::map<string, cmd_helper>::const_iterator it
1245 = cmdmap.find(argv[i]);
1247 // check for X11 -geometry option
1248 if (support::compare(argv[i], "-geometry") == 0)
1249 geometryOption_ = true;
1251 // don't complain if not found - may be parsed later
1252 if (it == cmdmap.end())
1255 string arg((i + 1 < argc) ? argv[i + 1] : "");
1256 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1258 int const remove = 1 + it->second(arg, arg2);
1260 // Now, remove used arguments by shifting
1261 // the following ones remove places down.
1263 for (int j = i; j < argc; ++j)
1264 argv[j] = argv[j + remove];
1268 batch_command = batch;
1272 FuncStatus getStatus(FuncRequest const & action)
1274 return LyX::ref().lyxFunc().getStatus(action);
1278 void dispatch(FuncRequest const & action)
1280 LyX::ref().lyxFunc().dispatch(action);
1284 BufferList & theBufferList()
1286 return LyX::ref().bufferList();
1290 LyXFunc & theLyXFunc()
1292 return LyX::ref().lyxFunc();
1296 LyXServer & theLyXServer()
1298 // FIXME: this should not be use_gui dependent
1299 BOOST_ASSERT(use_gui);
1300 return LyX::ref().server();
1304 LyXServerSocket & theLyXServerSocket()
1306 // FIXME: this should not be use_gui dependent
1307 BOOST_ASSERT(use_gui);
1308 return LyX::ref().socket();
1312 kb_keymap & theTopLevelKeymap()
1314 BOOST_ASSERT(use_gui);
1315 return LyX::ref().topLevelKeymap();
1323 void assertion_failed(char const* a, char const* b, char const* c, long d)
1325 lyx::lyxerr << "Assertion failed: " << a << ' ' << b << ' ' << c << ' '