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 "AppleSpellChecker.h"
21 #include "AspellChecker.h"
23 #include "BufferList.h"
26 #include "ConverterCache.h"
27 #include "Converter.h"
28 #include "CutAndPaste.h"
29 #include "EnchantChecker.h"
31 #include "ErrorList.h"
33 #include "FuncStatus.h"
34 #include "HunspellChecker.h"
37 #include "LayoutFile.h"
40 #include "LyXAction.h"
42 #include "ModuleList.h"
45 #include "ServerSocket.h"
48 #include "frontends/alert.h"
49 #include "frontends/Application.h"
51 #include "graphics/Previews.h"
53 #include "support/lassert.h"
54 #include "support/debug.h"
55 #include "support/environment.h"
56 #include "support/ExceptionMessage.h"
57 #include "support/filetools.h"
58 #include "support/gettext.h"
59 #include "support/lstrings.h"
60 #include "support/Messages.h"
61 #include "support/os.h"
62 #include "support/Package.h"
63 #include "support/Path.h"
64 #include "support/Systemcall.h"
66 #include "support/bind.h"
67 #include <boost/scoped_ptr.hpp>
78 using namespace lyx::support;
82 namespace Alert = frontend::Alert;
83 namespace os = support::os;
87 // Are we using the GUI at all? We default to true and this is changed
88 // to false when the export feature is used.
93 // Tell what files can be silently overwritten during batch export.
94 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
95 // Unless specified on command line (through the -f switch) or through the
96 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
98 OverwriteFiles force_overwrite = UNSPECIFIED;
103 // Filled with the command line arguments "foo" of "-sysdir foo" or
105 string cl_system_support;
106 string cl_user_support;
110 LyX * singleton_ = 0;
112 void showFileError(string const & error)
114 Alert::warning(_("Could not read configuration file"),
115 bformat(_("Error while reading the configuration file\n%1$s.\n"
116 "Please check your installation."), from_utf8(error)));
120 void reconfigureUserLyXDir()
122 string const configure_command = package().configure_command();
124 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
125 PathChanger p(package().user_support());
127 one.startscript(Systemcall::Wait, configure_command);
128 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
133 /// The main application class private implementation.
136 Impl() : spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
138 // Set the default User Interface language as soon as possible.
139 // The language used will be derived from the environment
141 messages_["GUI"] = Messages();
146 delete apple_spell_checker_;
147 delete aspell_checker_;
148 delete enchant_checker_;
149 delete hunspell_checker_;
153 BufferList buffer_list_;
155 KeyMap toplevel_keymap_;
157 CmdDef toplevel_cmddef_;
159 boost::scoped_ptr<Server> lyx_server_;
161 boost::scoped_ptr<ServerSocket> lyx_socket_;
163 boost::scoped_ptr<frontend::Application> application_;
164 /// lyx session, containing lastfiles, lastfilepos, and lastopened
165 boost::scoped_ptr<Session> session_;
167 /// Files to load at start.
168 vector<string> files_to_load_;
170 /// The messages translators.
171 map<string, Messages> messages_;
173 /// The file converters.
174 Converters converters_;
176 // The system converters copy after reading lyxrc.defaults.
177 Converters system_converters_;
182 Movers system_movers_;
184 /// has this user started lyx for the first time?
186 /// the parsed command line batch command if any
187 vector<string> batch_commands;
190 graphics::Previews preview_;
192 SpellChecker * spell_checker_;
194 SpellChecker * apple_spell_checker_;
196 SpellChecker * aspell_checker_;
198 SpellChecker * enchant_checker_;
200 SpellChecker * hunspell_checker_;
204 frontend::Application * theApp()
207 return singleton_->pimpl_->application_.get();
220 void lyx_exit(int exit_code)
223 // Something wrong happened so better save everything, just in
228 // Properly crash in debug mode in order to get a useful backtrace.
232 // In release mode, try to exit gracefully.
234 theApp()->exit(exit_code);
248 Messages & LyX::messages(string const & language)
250 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
252 if (it != pimpl_->messages_.end())
255 pair<map<string, Messages>::iterator, bool> result =
256 pimpl_->messages_.insert(make_pair(language, Messages(language)));
258 LASSERT(result.second, /**/);
259 return result.first->second;
263 void setRcGuiLanguage()
265 LASSERT(singleton_, /**/);
266 if (lyxrc.gui_language == "auto")
268 Language const * language = languages.getLanguage(lyxrc.gui_language);
270 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << language->code());
271 if (!setEnv("LANGUAGE", language->code()))
272 LYXERR(Debug::LOCALE, "\t... failed!");
274 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
275 if (!setEnv("LC_ALL", "en_US"))
276 LYXERR(Debug::LOCALE, "\t... failed!");
278 singleton_->pimpl_->messages_["GUI"] = Messages();
282 int LyX::exec(int & argc, char * argv[])
284 // Minimal setting of locale before parsing command line
286 init_package(os::utf8_argv(0), string(), string(),
287 top_build_dir_is_one_level_up);
288 } catch (ExceptionMessage const & message) {
289 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
293 // Here we need to parse the command line. At least
294 // we need to parse for "-dbg" and "-help"
295 easyParse(argc, argv);
298 init_package(os::utf8_argv(0),
299 cl_system_support, cl_user_support,
300 top_build_dir_is_one_level_up);
301 } catch (ExceptionMessage const & message) {
302 if (message.type_ == ErrorException) {
303 Alert::error(message.title_, message.details_);
305 } else if (message.type_ == WarningException) {
306 Alert::warning(message.title_, message.details_);
310 // Reinit the messages machinery in case package() knows
311 // something interesting about the locale directory.
315 // FIXME: create a ConsoleApplication
316 int exit_status = init(argc, argv);
322 // this is correct, since return values are inverted.
323 exit_status = !loadFiles();
325 if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
330 BufferList::iterator begin = pimpl_->buffer_list_.begin();
332 bool final_success = false;
333 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
335 if (buf != buf->masterBuffer())
337 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
338 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
340 for (; bcit != bcend; bcit++) {
341 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
342 buf->dispatch(*bcit, dr);
343 final_success |= !dr.error();
347 return !final_success;
350 // Let the frontend parse and remove all arguments that it knows
351 pimpl_->application_.reset(createApplication(argc, argv));
353 // Reestablish our defaults, as Qt overwrites them
354 // after createApplication()
357 // Parse and remove all known arguments in the LyX singleton
358 // Give an error for all remaining ones.
359 int exit_status = init(argc, argv);
361 // Kill the application object before exiting.
362 pimpl_->application_.reset();
369 /* Create a CoreApplication class that will provide the main event loop
370 * and the socket callback registering. With Qt4, only QtCore
371 * library would be needed.
372 * When this is done, a server_mode could be created and the following two
373 * line would be moved out from here.
375 // Note: socket callback must be registered after init(argc, argv)
376 // such that package().temp_dir() is properly initialized.
377 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
378 pimpl_->lyx_socket_.reset(new ServerSocket(
379 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
381 // Start the real execution loop.
382 exit_status = pimpl_->application_->exec();
390 void LyX::prepareExit()
392 // Clear the clipboard and selection stack:
393 cap::clearCutStack();
394 cap::clearSelection();
396 // Write the index file of the converter cache
397 ConverterCache::get().writeIndex();
399 // close buffers first
400 pimpl_->buffer_list_.closeAll();
402 // register session changes and shutdown server and socket
404 if (pimpl_->session_)
405 pimpl_->session_->writeFile();
406 pimpl_->session_.reset();
407 pimpl_->lyx_server_.reset();
408 pimpl_->lyx_socket_.reset();
411 // do any other cleanup procedures now
412 if (package().temp_dir() != package().system_temp_dir()) {
413 string const abs_tmpdir = package().temp_dir().absFileName();
414 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
415 docstring const msg =
416 bformat(_("%1$s does not appear like a LyX created temporary directory."),
417 from_utf8(abs_tmpdir));
418 Alert::warning(_("Cannot remove temporary directory"), msg);
420 LYXERR(Debug::INFO, "Deleting tmp dir "
421 << package().temp_dir().absFileName());
422 if (!package().temp_dir().destroyDirectory()) {
423 docstring const msg =
424 bformat(_("Unable to remove the temporary directory %1$s"),
425 from_utf8(package().temp_dir().absFileName()));
426 Alert::warning(_("Unable to remove temporary directory"), msg);
431 // Kill the application object before exiting. This avoids crashes
432 // when exiting on Linux.
433 if (pimpl_->application_)
434 pimpl_->application_.reset();
438 void LyX::earlyExit(int status)
440 LASSERT(pimpl_->application_.get(), /**/);
441 // LyX::pimpl_::application_ is not initialised at this
442 // point so it's safe to just exit after some cleanup.
448 int LyX::init(int & argc, char * argv[])
450 // check for any spurious extra arguments
451 // other than documents
452 for (int argi = 1; argi < argc ; ++argi) {
453 if (argv[argi][0] == '-') {
455 bformat(_("Wrong command line option `%1$s'. Exiting."),
456 from_utf8(os::utf8_argv(argi)))) << endl;
461 // Initialization of LyX (reads lyxrc and more)
462 LYXERR(Debug::INIT, "Initializing LyX::init...");
463 bool success = init();
464 LYXERR(Debug::INIT, "Initializing LyX::init...done");
468 // Remaining arguments are assumed to be files to load.
469 for (int argi = 1; argi < argc; ++argi)
470 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
473 pimpl_->files_to_load_.push_back(
474 i18nLibFileSearch("examples", "splash.lyx").absFileName());
481 bool LyX::loadFiles()
483 LASSERT(!use_gui, /**/);
485 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
486 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
488 for (; it != end; ++it) {
489 // get absolute path of file and add ".lyx" to
490 // the filename if necessary
491 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
497 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName(), false);
498 if (buf->loadLyXFile(fname)) {
499 ErrorList const & el = buf->errorList("Parse");
501 for_each(el.begin(), el.end(),
502 bind(&LyX::printError, this, _1));
505 pimpl_->buffer_list_.release(buf);
513 void execBatchCommands()
515 LASSERT(singleton_, /**/);
516 singleton_->execCommands();
520 void LyX::execCommands()
522 // The advantage of doing this here is that the event loop
523 // is already started. So any need for interaction will be
526 // if reconfiguration is needed.
527 if (LayoutFileList::get().empty()) {
528 switch (Alert::prompt(
529 _("No textclass is found"),
530 _("LyX will only have minimal functionality because no textclasses "
531 "have been found. You can either try to reconfigure LyX normally, "
532 "try to reconfigure using only the defaults, or continue."),
539 // regular reconfigure
540 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
543 // reconfigure --without-latex-config
544 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
545 " --without-latex-config"));
552 // create the first main window
553 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
555 if (!pimpl_->files_to_load_.empty()) {
556 // if some files were specified at command-line we assume that the
557 // user wants to edit *these* files and not to restore the session.
558 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
560 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
562 // clear this list to save a few bytes of RAM
563 pimpl_->files_to_load_.clear();
565 pimpl_->application_->restoreGuiSession();
567 // Execute batch commands if available
568 if (pimpl_->batch_commands.empty())
571 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
572 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
573 for (; bcit != bcend; bcit++) {
574 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
575 lyx::dispatch(lyxaction.lookupFunc(*bcit));
583 The SIGHUP signal does not exist on Windows and does not need to be handled.
585 Windows handles SIGFPE and SIGSEGV signals as expected.
587 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
588 cause a new thread to be spawned. This may well result in unexpected
589 behaviour by the single-threaded LyX.
591 SIGTERM signals will come only from another process actually sending
592 that signal using 'raise' in Windows' POSIX compatability layer. It will
593 not come from the general "terminate process" methods that everyone
594 actually uses (and which can't be trapped). Killing an app 'politely' on
595 Windows involves first sending a WM_CLOSE message, something that is
596 caught already by the Qt frontend.
598 For more information see:
600 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
601 ...signals are mostly useless on Windows for a variety of reasons that are
604 'UNIX Application Migration Guide, Chapter 9'
605 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
607 'How To Terminate an Application "Cleanly" in Win32'
608 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
612 static void error_handler(int err_sig)
614 // Throw away any signals other than the first one received.
615 static sig_atomic_t handling_error = false;
618 handling_error = true;
620 // We have received a signal indicating a fatal error, so
621 // try and save the data ASAP.
624 // These lyxerr calls may or may not work:
626 // Signals are asynchronous, so the main program may be in a very
627 // fragile state when a signal is processed and thus while a signal
628 // handler function executes.
629 // In general, therefore, we should avoid performing any
630 // I/O operations or calling most library and system functions from
633 // This shouldn't matter here, however, as we've already invoked
639 msg = _("SIGHUP signal caught!\nBye.");
643 msg = _("SIGFPE signal caught!\nBye.");
646 msg = _("SIGSEGV signal caught!\n"
647 "Sorry, you have found a bug in LyX, "
648 "hope you have not lost any data.\n"
649 "Please read the bug-reporting instructions "
650 "in 'Help->Introduction' and send us a bug report, "
651 "if necessary. Thanks !\nBye.");
660 lyxerr << "\nlyx: " << msg << endl;
661 // try to make a GUI message
662 Alert::error(_("LyX crashed!"), msg);
665 // Deinstall the signal handlers
667 signal(SIGHUP, SIG_DFL);
669 signal(SIGINT, SIG_DFL);
670 signal(SIGFPE, SIG_DFL);
671 signal(SIGSEGV, SIG_DFL);
672 signal(SIGTERM, SIG_DFL);
675 if (err_sig == SIGSEGV ||
676 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
678 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
681 // with abort() it crashes again.
694 void LyX::printError(ErrorItem const & ei)
696 docstring tmp = _("LyX: ") + ei.error + char_type(':')
698 cerr << to_utf8(tmp) << endl;
705 signal(SIGHUP, error_handler);
707 signal(SIGFPE, error_handler);
708 signal(SIGSEGV, error_handler);
709 signal(SIGINT, error_handler);
710 signal(SIGTERM, error_handler);
711 // SIGPIPE can be safely ignored.
713 lyxrc.tempdir_path = package().temp_dir().absFileName();
714 lyxrc.document_path = package().document_dir().absFileName();
716 if (lyxrc.example_path.empty()) {
717 lyxrc.example_path = addPath(package().system_support().absFileName(),
720 if (lyxrc.template_path.empty()) {
721 lyxrc.template_path = addPath(package().system_support().absFileName(),
726 // Read configuration files
729 // This one may have been distributed along with LyX.
730 if (!readRcFile("lyxrc.dist"))
733 // Set the language defined by the distributor.
736 // Set the PATH correctly.
737 #if !defined (USE_POSIX_PACKAGING)
738 // Add the directory containing the LyX executable to the path
739 // so that LyX can find things like tex2lyx.
740 if (package().build_support().empty())
741 prependEnvPath("PATH", package().binary_dir().absFileName());
743 if (!lyxrc.path_prefix.empty())
744 prependEnvPath("PATH", lyxrc.path_prefix);
746 // Check that user LyX directory is ok.
747 if (queryUserLyXDir(package().explicit_user_support()))
748 reconfigureUserLyXDir();
751 // No need for a splash when there is no GUI
753 // Default is to overwrite the main file during export, unless
754 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
755 if (force_overwrite == UNSPECIFIED) {
756 string const what = getEnv("LYX_FORCE_OVERWRITE");
758 force_overwrite = ALL_FILES;
759 else if (what == "none")
760 force_overwrite = NO_FILES;
762 force_overwrite = MAIN_FILE;
766 // This one is generated in user_support directory by lib/configure.py.
767 if (!readRcFile("lyxrc.defaults"))
770 // Query the OS to know what formats are viewed natively
771 formats.setAutoOpen();
773 // Read lyxrc.dist again to be able to override viewer auto-detection.
774 readRcFile("lyxrc.dist");
776 // Set again the language defined by the distributor.
779 system_lyxrc = lyxrc;
780 system_formats = formats;
781 pimpl_->system_converters_ = pimpl_->converters_;
782 pimpl_->system_movers_ = pimpl_->movers_;
783 system_lcolor = lcolor;
785 // This one is edited through the preferences dialog.
786 if (!readRcFile("preferences"))
789 if (!readEncodingsFile("encodings", "unicodesymbols"))
791 if (!readLanguagesFile("languages"))
794 // Set the language defined by the user.
797 LYXERR(Debug::INIT, "Reading layouts...");
799 LayoutFileList::get().read();
801 theModuleList.read();
803 // read keymap and ui files in batch mode as well
804 // because InsetInfo needs to know these to produce
805 // the correct output
807 // Set up command definitions
808 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
811 pimpl_->toplevel_keymap_.read("site");
812 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
813 // load user bind file user.bind
814 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
816 if (lyxerr.debugging(Debug::LYXRC))
819 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
820 if (!lyxrc.path_prefix.empty())
821 prependEnvPath("PATH", lyxrc.path_prefix);
823 FileName const document_path(lyxrc.document_path);
824 if (document_path.exists() && document_path.isDirectory())
825 package().document_dir() = document_path;
827 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
828 if (package().temp_dir().empty()) {
829 Alert::error(_("Could not create temporary directory"),
830 bformat(_("Could not create a temporary directory in\n"
832 "Make sure that this path exists and is writable and try again."),
833 from_utf8(lyxrc.tempdir_path)));
834 // createLyXTmpDir() tries sufficiently hard to create a
835 // usable temp dir, so the probability to come here is
836 // close to zero. We therefore don't try to overcome this
837 // problem with e.g. asking the user for a new path and
838 // trying again but simply exit.
842 LYXERR(Debug::INIT, "LyX tmp dir: `"
843 << package().temp_dir().absFileName() << '\'');
845 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
846 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
848 // This must happen after package initialization and after lyxrc is
849 // read, therefore it can't be done by a static object.
850 ConverterCache::init();
856 void emergencyCleanup()
858 // what to do about tmpfiles is non-obvious. we would
859 // like to delete any we find, but our lyxdir might
860 // contain documents etc. which might be helpful on
863 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
865 if (singleton_->pimpl_->lyx_server_)
866 singleton_->pimpl_->lyx_server_->emergencyCleanup();
867 singleton_->pimpl_->lyx_server_.reset();
868 singleton_->pimpl_->lyx_socket_.reset();
873 // return true if file does not exist or is older than configure.py.
874 static bool needsUpdate(string const & file)
876 // We cannot initialize configure_script directly because the package
877 // is not initialized yet when static objects are constructed.
878 static FileName configure_script;
879 static bool firstrun = true;
882 FileName(addName(package().system_support().absFileName(),
888 FileName(addName(package().user_support().absFileName(), file));
889 return !absfile.exists()
890 || configure_script.lastModified() > absfile.lastModified();
894 bool LyX::queryUserLyXDir(bool explicit_userdir)
896 // Does user directory exist?
897 FileName const sup = package().user_support();
898 if (sup.exists() && sup.isDirectory()) {
901 return needsUpdate("lyxrc.defaults")
902 || needsUpdate("lyxmodules.lst")
903 || needsUpdate("textclass.lst")
904 || needsUpdate("packages.lst");
907 first_start = !explicit_userdir;
909 // If the user specified explicitly a directory, ask whether
910 // to create it. If the user says "no", then exit.
911 if (explicit_userdir &&
913 _("Missing user LyX directory"),
914 bformat(_("You have specified a non-existent user "
915 "LyX directory, %1$s.\n"
916 "It is needed to keep your own configuration."),
917 from_utf8(package().user_support().absFileName())),
919 _("&Create directory"),
921 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
922 earlyExit(EXIT_FAILURE);
925 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
926 from_utf8(sup.absFileName()))) << endl;
928 if (!sup.createDirectory(0755)) {
929 // Failed, so let's exit.
930 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
932 earlyExit(EXIT_FAILURE);
939 bool LyX::readRcFile(string const & name)
941 LYXERR(Debug::INIT, "About to read " << name << "... ");
943 FileName const lyxrc_path = libFileSearch(string(), name);
944 if (!lyxrc_path.empty()) {
945 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
946 if (lyxrc.read(lyxrc_path) < 0) {
951 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
956 // Read the languages file `name'
957 bool LyX::readLanguagesFile(string const & name)
959 LYXERR(Debug::INIT, "About to read " << name << "...");
961 FileName const lang_path = libFileSearch(string(), name);
962 if (lang_path.empty()) {
966 languages.read(lang_path);
971 // Read the encodings file `name'
972 bool LyX::readEncodingsFile(string const & enc_name,
973 string const & symbols_name)
975 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
976 << symbols_name << "...");
978 FileName const symbols_path = libFileSearch(string(), symbols_name);
979 if (symbols_path.empty()) {
980 showFileError(symbols_name);
984 FileName const enc_path = libFileSearch(string(), enc_name);
985 if (enc_path.empty()) {
986 showFileError(enc_name);
989 encodings.read(enc_path, symbols_path);
996 /// return the the number of arguments consumed
997 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
999 int parse_dbg(string const & arg, string const &, string &)
1002 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1003 Debug::showTags(lyxerr);
1006 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1008 lyxerr.setLevel(Debug::value(arg));
1009 Debug::showLevel(lyxerr, lyxerr.level());
1014 int parse_help(string const &, string const &, string &)
1017 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1018 "Command line switches (case sensitive):\n"
1019 "\t-help summarize LyX usage\n"
1020 "\t-userdir dir set user directory to dir\n"
1021 "\t-sysdir dir set system directory to dir\n"
1022 "\t-geometry WxH+X+Y set geometry of the main window\n"
1023 "\t-dbg feature[,feature]...\n"
1024 " select the features to debug.\n"
1025 " Type `lyx -dbg' to see the list of features\n"
1026 "\t-x [--execute] command\n"
1027 " where command is a lyx command.\n"
1028 "\t-e [--export] fmt\n"
1029 " where fmt is the export format of choice.\n"
1030 " Look on Tools->Preferences->File formats->Format\n"
1031 " to get an idea which parameters should be passed.\n"
1032 " Note that the order of -e and -x switches matters.\n"
1033 "\t-i [--import] fmt file.xxx\n"
1034 " where fmt is the import format of choice\n"
1035 " and file.xxx is the file to be imported.\n"
1036 "\t-f [--force-overwrite] what\n"
1037 " where what is either `all', `main' or `none',\n"
1038 " specifying whether all files, main file only, or no files,\n"
1039 " respectively, are to be overwritten during a batch export.\n"
1040 " Anything else is equivalent to `all', but is not consumed.\n"
1041 "\t-batch execute commands without launching GUI and exit.\n"
1042 "\t-version summarize version and build info\n"
1043 "Check the LyX man page for more details.")) << endl;
1049 int parse_version(string const &, string const &, string &)
1051 lyxerr << "LyX " << lyx_version
1052 << " (" << lyx_release_date << ")" << endl;
1053 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1055 lyxerr << lyx_version_info << endl;
1061 int parse_sysdir(string const & arg, string const &, string &)
1064 Alert::error(_("No system directory"),
1065 _("Missing directory for -sysdir switch"));
1068 cl_system_support = arg;
1073 int parse_userdir(string const & arg, string const &, string &)
1076 Alert::error(_("No user directory"),
1077 _("Missing directory for -userdir switch"));
1080 cl_user_support = arg;
1085 int parse_execute(string const & arg, string const &, string & batch)
1088 Alert::error(_("Incomplete command"),
1089 _("Missing command string after --execute switch"));
1097 int parse_export(string const & type, string const &, string & batch)
1100 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1101 "--export switch")) << endl;
1104 batch = "buffer-export " + type;
1110 int parse_import(string const & type, string const & file, string & batch)
1113 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1114 "--import switch")) << endl;
1118 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1122 batch = "buffer-import " + type + ' ' + file;
1127 int parse_geometry(string const & arg1, string const &, string &)
1130 // don't remove "-geometry", it will be pruned out later in the
1131 // frontend if need be.
1136 int parse_batch(string const &, string const &, string &)
1143 int parse_force(string const & arg, string const &, string &)
1146 force_overwrite = ALL_FILES;
1148 } else if (arg == "main") {
1149 force_overwrite = MAIN_FILE;
1151 } else if (arg == "none") {
1152 force_overwrite = NO_FILES;
1155 force_overwrite = ALL_FILES;
1163 void LyX::easyParse(int & argc, char * argv[])
1165 map<string, cmd_helper> cmdmap;
1167 cmdmap["-dbg"] = parse_dbg;
1168 cmdmap["-help"] = parse_help;
1169 cmdmap["--help"] = parse_help;
1170 cmdmap["-version"] = parse_version;
1171 cmdmap["--version"] = parse_version;
1172 cmdmap["-sysdir"] = parse_sysdir;
1173 cmdmap["-userdir"] = parse_userdir;
1174 cmdmap["-x"] = parse_execute;
1175 cmdmap["--execute"] = parse_execute;
1176 cmdmap["-e"] = parse_export;
1177 cmdmap["--export"] = parse_export;
1178 cmdmap["-i"] = parse_import;
1179 cmdmap["--import"] = parse_import;
1180 cmdmap["-geometry"] = parse_geometry;
1181 cmdmap["-batch"] = parse_batch;
1182 cmdmap["-f"] = parse_force;
1183 cmdmap["--force-overwrite"] = parse_force;
1185 for (int i = 1; i < argc; ++i) {
1186 map<string, cmd_helper>::const_iterator it
1187 = cmdmap.find(argv[i]);
1189 // don't complain if not found - may be parsed later
1190 if (it == cmdmap.end())
1194 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1196 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1199 int const remove = 1 + it->second(arg, arg2, batch);
1201 pimpl_->batch_commands.push_back(batch);
1203 // Now, remove used arguments by shifting
1204 // the following ones remove places down.
1206 os::remove_internal_args(i, remove);
1208 for (int j = i; j < argc; ++j)
1209 argv[j] = argv[j + remove];
1216 FuncStatus getStatus(FuncRequest const & action)
1218 LASSERT(theApp(), /**/);
1219 return theApp()->getStatus(action);
1223 void dispatch(FuncRequest const & action)
1225 LASSERT(theApp(), /**/);
1226 return theApp()->dispatch(action);
1230 void dispatch(FuncRequest const & action, DispatchResult & dr)
1232 LASSERT(theApp(), /**/);
1233 return theApp()->dispatch(action, dr);
1237 BufferList & theBufferList()
1239 LASSERT(singleton_, /**/);
1240 return singleton_->pimpl_->buffer_list_;
1244 Server & theServer()
1246 // FIXME: this should not be use_gui dependent
1247 LASSERT(use_gui, /**/);
1248 LASSERT(singleton_, /**/);
1249 return *singleton_->pimpl_->lyx_server_.get();
1253 ServerSocket & theServerSocket()
1255 // FIXME: this should not be use_gui dependent
1256 LASSERT(use_gui, /**/);
1257 LASSERT(singleton_, /**/);
1258 return *singleton_->pimpl_->lyx_socket_.get();
1262 KeyMap & theTopLevelKeymap()
1264 LASSERT(singleton_, /**/);
1265 return singleton_->pimpl_->toplevel_keymap_;
1269 Converters & theConverters()
1271 LASSERT(singleton_, /**/);
1272 return singleton_->pimpl_->converters_;
1276 Converters & theSystemConverters()
1278 LASSERT(singleton_, /**/);
1279 return singleton_->pimpl_->system_converters_;
1283 Movers & theMovers()
1285 LASSERT(singleton_, /**/);
1286 return singleton_->pimpl_->movers_;
1290 Mover const & getMover(string const & fmt)
1292 LASSERT(singleton_, /**/);
1293 return singleton_->pimpl_->movers_(fmt);
1297 void setMover(string const & fmt, string const & command)
1299 LASSERT(singleton_, /**/);
1300 singleton_->pimpl_->movers_.set(fmt, command);
1304 Movers & theSystemMovers()
1306 LASSERT(singleton_, /**/);
1307 return singleton_->pimpl_->system_movers_;
1311 Messages const & getMessages(string const & language)
1313 LASSERT(singleton_, /**/);
1314 return singleton_->messages(language);
1318 Messages const & getGuiMessages()
1320 LASSERT(singleton_, /**/);
1321 return singleton_->pimpl_->messages_["GUI"];
1325 graphics::Previews & thePreviews()
1327 LASSERT(singleton_, /**/);
1328 return singleton_->pimpl_->preview_;
1332 Session & theSession()
1334 LASSERT(singleton_, /**/);
1335 return *singleton_->pimpl_->session_.get();
1339 CmdDef & theTopLevelCmdDef()
1341 LASSERT(singleton_, /**/);
1342 return singleton_->pimpl_->toplevel_cmddef_;
1346 SpellChecker * theSpellChecker()
1348 if (!singleton_->pimpl_->spell_checker_)
1350 return singleton_->pimpl_->spell_checker_;
1354 void setSpellChecker()
1356 #if defined(USE_MACOSX_PACKAGING)
1357 if (lyxrc.spellchecker == "native") {
1358 if (!singleton_->pimpl_->apple_spell_checker_)
1359 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker();
1360 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1364 #if defined(USE_ASPELL)
1365 if (lyxrc.spellchecker == "aspell") {
1366 if (!singleton_->pimpl_->aspell_checker_)
1367 singleton_->pimpl_->aspell_checker_ = new AspellChecker();
1368 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1372 #if defined(USE_ENCHANT)
1373 if (lyxrc.spellchecker == "enchant") {
1374 if (!singleton_->pimpl_->enchant_checker_)
1375 singleton_->pimpl_->enchant_checker_ = new EnchantChecker();
1376 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1380 #if defined(USE_HUNSPELL)
1381 if (lyxrc.spellchecker == "hunspell") {
1382 if (!singleton_->pimpl_->hunspell_checker_)
1383 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker();
1384 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1388 singleton_->pimpl_->spell_checker_ = 0;