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 "LaTeXFonts.h"
38 #include "LayoutFile.h"
41 #include "LyXAction.h"
43 #include "ModuleList.h"
46 #include "ServerSocket.h"
50 #include "frontends/alert.h"
51 #include "frontends/Application.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/PathChanger.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 // We default to open documents in an already running instance, provided that
94 // the lyxpipe has been setup. This can be overridden either on the command
95 // line or through preference settings.
97 RunMode run_mode = PREFERRED;
100 // Tell what files can be silently overwritten during batch export.
101 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
102 // Unless specified on command line (through the -f switch) or through the
103 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
105 OverwriteFiles force_overwrite = UNSPECIFIED;
110 // Filled with the command line arguments "foo" of "-sysdir foo" or
112 string cl_system_support;
113 string cl_user_support;
117 LyX * singleton_ = 0;
119 void showFileError(string const & error)
121 Alert::warning(_("Could not read configuration file"),
122 bformat(_("Error while reading the configuration file\n%1$s.\n"
123 "Please check your installation."), from_utf8(error)));
127 void reconfigureUserLyXDir()
129 string const configure_command = package().configure_command();
131 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
132 PathChanger p(package().user_support());
134 one.startscript(Systemcall::Wait, configure_command);
135 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
140 /// The main application class private implementation.
143 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
149 delete apple_spell_checker_;
150 delete aspell_checker_;
151 delete enchant_checker_;
152 delete hunspell_checker_;
156 BufferList buffer_list_;
158 KeyMap toplevel_keymap_;
160 CmdDef toplevel_cmddef_;
162 boost::scoped_ptr<Server> lyx_server_;
164 boost::scoped_ptr<ServerSocket> lyx_socket_;
166 boost::scoped_ptr<frontend::Application> application_;
167 /// lyx session, containing lastfiles, lastfilepos, and lastopened
168 boost::scoped_ptr<Session> session_;
170 /// Files to load at start.
171 vector<string> files_to_load_;
173 /// The messages translators.
174 map<string, Messages> messages_;
176 /// The file converters.
177 Converters converters_;
179 // The system converters copy after reading lyxrc.defaults.
180 Converters system_converters_;
185 Movers system_movers_;
187 /// has this user started lyx for the first time?
189 /// the parsed command line batch command if any
190 vector<string> batch_commands;
193 LaTeXFonts * latexfonts_;
196 SpellChecker * spell_checker_;
198 SpellChecker * apple_spell_checker_;
200 SpellChecker * aspell_checker_;
202 SpellChecker * enchant_checker_;
204 SpellChecker * hunspell_checker_;
209 frontend::Application * theApp()
212 return singleton_->pimpl_->application_.get();
222 WordList::cleanupWordLists();
226 void lyx_exit(int exit_code)
229 // Something wrong happened so better save everything, just in
234 // Properly crash in debug mode in order to get a useful backtrace.
238 // In release mode, try to exit gracefully.
240 theApp()->exit(exit_code);
254 Messages & LyX::messages(string const & language)
256 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
258 if (it != pimpl_->messages_.end())
261 pair<map<string, Messages>::iterator, bool> result =
262 pimpl_->messages_.insert(make_pair(language, Messages(language)));
264 LATTEST(result.second);
265 return result.first->second;
269 int LyX::exec(int & argc, char * argv[])
271 // Minimal setting of locale before parsing command line
273 init_package(os::utf8_argv(0), string(), string());
274 // we do not get to this point when init_package throws an exception
276 } catch (ExceptionMessage const & message) {
277 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
280 // Here we need to parse the command line. At least
281 // we need to parse for "-dbg" and "-help"
282 easyParse(argc, argv);
285 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
286 } catch (ExceptionMessage const & message) {
287 if (message.type_ == ErrorException) {
288 Alert::error(message.title_, message.details_);
290 } else if (message.type_ == WarningException) {
291 Alert::warning(message.title_, message.details_);
295 // Reinit the messages machinery in case package() knows
296 // something interesting about the locale directory.
300 // FIXME: create a ConsoleApplication
301 int exit_status = init(argc, argv);
307 // this is correct, since return values are inverted.
308 exit_status = !loadFiles();
310 if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
315 BufferList::iterator begin = pimpl_->buffer_list_.begin();
317 bool final_success = false;
318 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
320 if (buf != buf->masterBuffer())
322 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
323 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
325 for (; bcit != bcend; ++bcit) {
326 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
327 buf->dispatch(*bcit, dr);
328 final_success |= !dr.error();
332 return !final_success;
335 // Let the frontend parse and remove all arguments that it knows
336 pimpl_->application_.reset(createApplication(argc, argv));
338 // Reestablish our defaults, as Qt overwrites them
339 // after createApplication()
342 // Parse and remove all known arguments in the LyX singleton
343 // Give an error for all remaining ones.
344 int exit_status = init(argc, argv);
346 // Kill the application object before exiting.
347 pimpl_->application_.reset();
353 // If not otherwise specified by a command line option or
354 // by preferences, we default to reuse a running instance.
355 if (run_mode == PREFERRED)
356 run_mode = USE_REMOTE;
359 /* Create a CoreApplication class that will provide the main event loop
360 * and the socket callback registering. With Qt4, only QtCore
361 * library would be needed.
362 * When this is done, a server_mode could be created and the following two
363 * line would be moved out from here.
364 * However, note that the first of the two lines below triggers the
365 * "single instance" behavior, which should occur right at this point.
367 // Note: socket callback must be registered after init(argc, argv)
368 // such that package().temp_dir() is properly initialized.
369 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
370 pimpl_->lyx_socket_.reset(new ServerSocket(
371 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
373 // Start the real execution loop.
374 if (!theServer().deferredLoadingToOtherInstance())
375 exit_status = pimpl_->application_->exec();
376 else if (!pimpl_->files_to_load_.empty()) {
377 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
378 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
379 lyxerr << _("The following files could not be loaded:") << endl;
380 for (; it != end; ++it)
381 lyxerr << *it << endl;
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 pimpl_->application_.reset();
437 void LyX::earlyExit(int status)
439 LATTEST(pimpl_->application_.get());
440 // LyX::pimpl_::application_ is not initialised at this
441 // point so it's safe to just exit after some cleanup.
447 int LyX::init(int & argc, char * argv[])
449 // check for any spurious extra arguments
450 // other than documents
451 for (int argi = 1; argi < argc ; ++argi) {
452 if (argv[argi][0] == '-') {
454 bformat(_("Wrong command line option `%1$s'. Exiting."),
455 from_utf8(os::utf8_argv(argi)))) << endl;
460 // Initialization of LyX (reads lyxrc and more)
461 LYXERR(Debug::INIT, "Initializing LyX::init...");
462 bool success = init();
463 LYXERR(Debug::INIT, "Initializing LyX::init...done");
467 // Remaining arguments are assumed to be files to load.
468 for (int argi = 1; argi < argc; ++argi)
469 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
471 if (!use_gui && pimpl_->files_to_load_.empty()) {
472 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
477 pimpl_->files_to_load_.push_back(
478 i18nLibFileSearch("examples", "splash.lyx").absFileName());
485 bool LyX::loadFiles()
489 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
490 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
492 for (; it != end; ++it) {
493 // get absolute path of file and add ".lyx" to
494 // the filename if necessary
495 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
501 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
502 if (buf->loadLyXFile() == Buffer::ReadSuccess) {
503 ErrorList const & el = buf->errorList("Parse");
505 for_each(el.begin(), el.end(),
506 bind(&LyX::printError, this, _1));
509 pimpl_->buffer_list_.release(buf);
510 docstring const error_message =
511 bformat(_("LyX failed to load the following file: %1$s"),
512 from_utf8(fname.absFileName()));
513 lyxerr << to_utf8(error_message) << endl;
521 void execBatchCommands()
524 singleton_->execCommands();
528 void LyX::execCommands()
530 // The advantage of doing this here is that the event loop
531 // is already started. So any need for interaction will be
534 // if reconfiguration is needed.
535 if (LayoutFileList::get().empty()) {
536 switch (Alert::prompt(
537 _("No textclass is found"),
538 _("LyX will only have minimal functionality because no textclasses "
539 "have been found. You can either try to reconfigure LyX normally, "
540 "try to reconfigure without checking your LaTeX installation, or continue."),
547 // regular reconfigure
548 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
551 // reconfigure --without-latex-config
552 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
553 " --without-latex-config"));
560 // create the first main window
561 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
563 if (!pimpl_->files_to_load_.empty()) {
564 // if some files were specified at command-line we assume that the
565 // user wants to edit *these* files and not to restore the session.
566 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
568 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
570 // clear this list to save a few bytes of RAM
571 pimpl_->files_to_load_.clear();
573 pimpl_->application_->restoreGuiSession();
575 // Execute batch commands if available
576 if (pimpl_->batch_commands.empty())
579 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
580 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
581 for (; bcit != bcend; ++bcit) {
582 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
583 lyx::dispatch(lyxaction.lookupFunc(*bcit));
591 The SIGHUP signal does not exist on Windows and does not need to be handled.
593 Windows handles SIGFPE and SIGSEGV signals as expected.
595 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
596 cause a new thread to be spawned. This may well result in unexpected
597 behaviour by the single-threaded LyX.
599 SIGTERM signals will come only from another process actually sending
600 that signal using 'raise' in Windows' POSIX compatability layer. It will
601 not come from the general "terminate process" methods that everyone
602 actually uses (and which can't be trapped). Killing an app 'politely' on
603 Windows involves first sending a WM_CLOSE message, something that is
604 caught already by the Qt frontend.
606 For more information see:
608 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
609 ...signals are mostly useless on Windows for a variety of reasons that are
612 'UNIX Application Migration Guide, Chapter 9'
613 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
615 'How To Terminate an Application "Cleanly" in Win32'
616 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
620 static void error_handler(int err_sig)
622 // Throw away any signals other than the first one received.
623 static sig_atomic_t handling_error = false;
626 handling_error = true;
628 // We have received a signal indicating a fatal error, so
629 // try and save the data ASAP.
632 // These lyxerr calls may or may not work:
634 // Signals are asynchronous, so the main program may be in a very
635 // fragile state when a signal is processed and thus while a signal
636 // handler function executes.
637 // In general, therefore, we should avoid performing any
638 // I/O operations or calling most library and system functions from
641 // This shouldn't matter here, however, as we've already invoked
647 msg = _("SIGHUP signal caught!\nBye.");
651 msg = _("SIGFPE signal caught!\nBye.");
654 msg = _("SIGSEGV signal caught!\n"
655 "Sorry, you have found a bug in LyX, "
656 "hope you have not lost any data.\n"
657 "Please read the bug-reporting instructions "
658 "in 'Help->Introduction' and send us a bug report, "
659 "if necessary. Thanks!\nBye.");
668 lyxerr << "\nlyx: " << msg << endl;
669 // try to make a GUI message
670 Alert::error(_("LyX crashed!"), msg);
673 // Deinstall the signal handlers
675 signal(SIGHUP, SIG_DFL);
677 signal(SIGINT, SIG_DFL);
678 signal(SIGFPE, SIG_DFL);
679 signal(SIGSEGV, SIG_DFL);
680 signal(SIGTERM, SIG_DFL);
683 if (err_sig == SIGSEGV ||
684 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
686 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
689 // with abort() it crashes again.
702 void LyX::printError(ErrorItem const & ei)
704 docstring tmp = _("LyX: ") + ei.error + char_type(':')
706 cerr << to_utf8(tmp) << endl;
713 signal(SIGHUP, error_handler);
715 signal(SIGFPE, error_handler);
716 signal(SIGSEGV, error_handler);
717 signal(SIGINT, error_handler);
718 signal(SIGTERM, error_handler);
719 // SIGPIPE can be safely ignored.
721 lyxrc.tempdir_path = package().temp_dir().absFileName();
722 lyxrc.document_path = package().document_dir().absFileName();
724 if (lyxrc.example_path.empty()) {
725 lyxrc.example_path = addPath(package().system_support().absFileName(),
728 if (lyxrc.template_path.empty()) {
729 lyxrc.template_path = addPath(package().system_support().absFileName(),
733 // init LyXDir environment variable
734 string const lyx_dir = package().lyx_dir().absFileName();
735 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
736 if (!setEnv("LyXDir", lyx_dir))
737 LYXERR(Debug::INIT, "\t... failed!");
740 // Read configuration files
743 // This one may have been distributed along with LyX.
744 if (!readRcFile("lyxrc.dist"))
747 // Set the PATH correctly.
748 #if !defined (USE_POSIX_PACKAGING)
749 // Add the directory containing the LyX executable to the path
750 // so that LyX can find things like tex2lyx.
751 if (package().build_support().empty())
752 prependEnvPath("PATH", package().binary_dir().absFileName());
754 if (!lyxrc.path_prefix.empty())
755 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
757 // Check that user LyX directory is ok.
758 if (queryUserLyXDir(package().explicit_user_support()))
759 reconfigureUserLyXDir();
762 // No need for a splash when there is no GUI
764 // Default is to overwrite the main file during export, unless
765 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
766 if (force_overwrite == UNSPECIFIED) {
767 string const what = getEnv("LYX_FORCE_OVERWRITE");
769 force_overwrite = ALL_FILES;
770 else if (what == "none")
771 force_overwrite = NO_FILES;
773 force_overwrite = MAIN_FILE;
777 // This one is generated in user_support directory by lib/configure.py.
778 if (!readRcFile("lyxrc.defaults"))
781 // Query the OS to know what formats are viewed natively
782 formats.setAutoOpen();
784 // Read lyxrc.dist again to be able to override viewer auto-detection.
785 readRcFile("lyxrc.dist");
787 system_lyxrc = lyxrc;
788 system_formats = formats;
789 pimpl_->system_converters_ = pimpl_->converters_;
790 pimpl_->system_movers_ = pimpl_->movers_;
791 system_lcolor = lcolor;
793 // This one is edited through the preferences dialog.
794 if (!readRcFile("preferences", true))
797 if (!readEncodingsFile("encodings", "unicodesymbols"))
799 if (!readLanguagesFile("languages"))
802 LYXERR(Debug::INIT, "Reading layouts...");
804 LayoutFileList::get().read();
806 theModuleList.read();
808 // read keymap and ui files in batch mode as well
809 // because InsetInfo needs to know these to produce
810 // the correct output
812 // Set up command definitions
813 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
817 pimpl_->toplevel_keymap_.read("site");
818 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
819 // load user bind file user.bind
820 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
822 if (lyxerr.debugging(Debug::LYXRC))
825 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
826 if (!lyxrc.path_prefix.empty())
827 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
829 FileName const document_path(lyxrc.document_path);
830 if (document_path.exists() && document_path.isDirectory())
831 package().document_dir() = document_path;
833 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
834 if (package().temp_dir().empty()) {
835 Alert::error(_("Could not create temporary directory"),
836 bformat(_("Could not create a temporary directory in\n"
838 "Make sure that this path exists and is writable and try again."),
839 from_utf8(lyxrc.tempdir_path)));
840 // createLyXTmpDir() tries sufficiently hard to create a
841 // usable temp dir, so the probability to come here is
842 // close to zero. We therefore don't try to overcome this
843 // problem with e.g. asking the user for a new path and
844 // trying again but simply exit.
848 LYXERR(Debug::INIT, "LyX tmp dir: `"
849 << package().temp_dir().absFileName() << '\'');
851 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
852 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
854 // This must happen after package initialization and after lyxrc is
855 // read, therefore it can't be done by a static object.
856 ConverterCache::init();
862 void emergencyCleanup()
864 // what to do about tmpfiles is non-obvious. we would
865 // like to delete any we find, but our lyxdir might
866 // contain documents etc. which might be helpful on
869 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
871 if (singleton_->pimpl_->lyx_server_)
872 singleton_->pimpl_->lyx_server_->emergencyCleanup();
873 singleton_->pimpl_->lyx_server_.reset();
874 singleton_->pimpl_->lyx_socket_.reset();
879 // return true if file does not exist or is older than configure.py.
880 static bool needsUpdate(string const & file)
882 // We cannot initialize configure_script directly because the package
883 // is not initialized yet when static objects are constructed.
884 static FileName configure_script;
885 static bool firstrun = true;
888 FileName(addName(package().system_support().absFileName(),
894 FileName(addName(package().user_support().absFileName(), file));
895 return !absfile.exists()
896 || configure_script.lastModified() > absfile.lastModified();
900 bool LyX::queryUserLyXDir(bool explicit_userdir)
902 // Does user directory exist?
903 FileName const sup = package().user_support();
904 if (sup.exists() && sup.isDirectory()) {
907 return needsUpdate("lyxrc.defaults")
908 || needsUpdate("lyxmodules.lst")
909 || needsUpdate("textclass.lst")
910 || needsUpdate("packages.lst");
913 first_start = !explicit_userdir;
915 // If the user specified explicitly a directory, ask whether
916 // to create it. If the user says "no", then exit.
917 if (explicit_userdir &&
919 _("Missing user LyX directory"),
920 bformat(_("You have specified a non-existent user "
921 "LyX directory, %1$s.\n"
922 "It is needed to keep your own configuration."),
923 from_utf8(package().user_support().absFileName())),
925 _("&Create directory"),
927 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
928 earlyExit(EXIT_FAILURE);
931 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
932 from_utf8(sup.absFileName()))) << endl;
934 if (!sup.createDirectory(0755)) {
935 // Failed, so let's exit.
936 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
938 earlyExit(EXIT_FAILURE);
945 bool LyX::readRcFile(string const & name, bool check_format)
947 LYXERR(Debug::INIT, "About to read " << name << "... ");
949 FileName const lyxrc_path = libFileSearch(string(), name);
950 if (lyxrc_path.empty()) {
951 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
953 // This was the previous logic, but can it be right??
956 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
957 bool const success = lyxrc.read(lyxrc_path, check_format);
963 // Read the languages file `name'
964 bool LyX::readLanguagesFile(string const & name)
966 LYXERR(Debug::INIT, "About to read " << name << "...");
968 FileName const lang_path = libFileSearch(string(), name);
969 if (lang_path.empty()) {
973 languages.read(lang_path);
978 // Read the encodings file `name'
979 bool LyX::readEncodingsFile(string const & enc_name,
980 string const & symbols_name)
982 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
983 << symbols_name << "...");
985 FileName const symbols_path = libFileSearch(string(), symbols_name);
986 if (symbols_path.empty()) {
987 showFileError(symbols_name);
991 FileName const enc_path = libFileSearch(string(), enc_name);
992 if (enc_path.empty()) {
993 showFileError(enc_name);
996 encodings.read(enc_path, symbols_path);
1003 /// return the the number of arguments consumed
1004 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
1006 int parse_dbg(string const & arg, string const &, string &)
1009 cout << to_utf8(_("List of supported debug flags:")) << endl;
1010 Debug::showTags(cout);
1013 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1015 lyxerr.setLevel(Debug::value(arg));
1016 Debug::showLevel(lyxerr, lyxerr.level());
1021 int parse_help(string const &, string const &, string &)
1024 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1025 "Command line switches (case sensitive):\n"
1026 "\t-help summarize LyX usage\n"
1027 "\t-userdir dir set user directory to dir\n"
1028 "\t-sysdir dir set system directory to dir\n"
1029 "\t-geometry WxH+X+Y set geometry of the main window\n"
1030 "\t-dbg feature[,feature]...\n"
1031 " select the features to debug.\n"
1032 " Type `lyx -dbg' to see the list of features\n"
1033 "\t-x [--execute] command\n"
1034 " where command is a lyx command.\n"
1035 "\t-e [--export] fmt\n"
1036 " where fmt is the export format of choice. Look in\n"
1037 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1038 " to see which parameter (which differs from the format name\n"
1039 " in the File->Export menu) should be passed.\n"
1040 " Note that the order of -e and -x switches matters.\n"
1041 "\t-E [--export-to] fmt filename\n"
1042 " where fmt is the export format of choice (see --export),\n"
1043 " and filename is the destination filename.\n"
1044 "\t-i [--import] fmt file.xxx\n"
1045 " where fmt is the import format of choice\n"
1046 " and file.xxx is the file to be imported.\n"
1047 "\t-f [--force-overwrite] what\n"
1048 " where what is either `all', `main' or `none',\n"
1049 " specifying whether all files, main file only, or no files,\n"
1050 " respectively, are to be overwritten during a batch export.\n"
1051 " Anything else is equivalent to `all', but is not consumed.\n"
1052 "\t-n [--no-remote]\n"
1053 " open documents in a new instance\n"
1055 " open documents in an already running instance\n"
1056 " (a working lyxpipe is needed)\n"
1057 "\t-batch execute commands without launching GUI and exit.\n"
1058 "\t-version summarize version and build info\n"
1059 "Check the LyX man page for more details.")) << endl;
1065 int parse_version(string const &, string const &, string &)
1067 cout << "LyX " << lyx_version
1068 << " (" << lyx_release_date << ")" << endl;
1069 cout << to_utf8(bformat(_("Built on %1$s[[date]], %2$s[[time]]"),
1070 from_ascii(__DATE__), from_ascii(__TIME__))) << endl;
1072 cout << lyx_version_info << endl;
1078 int parse_sysdir(string const & arg, string const &, string &)
1081 Alert::error(_("No system directory"),
1082 _("Missing directory for -sysdir switch"));
1085 cl_system_support = arg;
1090 int parse_userdir(string const & arg, string const &, string &)
1093 Alert::error(_("No user directory"),
1094 _("Missing directory for -userdir switch"));
1097 cl_user_support = arg;
1102 int parse_execute(string const & arg, string const &, string & batch)
1105 Alert::error(_("Incomplete command"),
1106 _("Missing command string after --execute switch"));
1114 int parse_export_to(string const & type, string const & output_file, string & batch)
1117 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1118 "--export-to switch")) << endl;
1121 if (output_file.empty()) {
1122 lyxerr << to_utf8(_("Missing destination filename after "
1123 "--export-to switch")) << endl;
1126 batch = "buffer-export " + type + " " + output_file;
1132 int parse_export(string const & type, string const &, string & batch)
1135 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1136 "--export switch")) << endl;
1139 batch = "buffer-export " + type;
1145 int parse_import(string const & type, string const & file, string & batch)
1148 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1149 "--import switch")) << endl;
1153 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1156 batch = "buffer-import " + type + ' ' + file;
1161 int parse_geometry(string const & arg1, string const &, string &)
1164 // don't remove "-geometry", it will be pruned out later in the
1165 // frontend if need be.
1170 int parse_batch(string const &, string const &, string &)
1177 int parse_noremote(string const &, string const &, string &)
1179 run_mode = NEW_INSTANCE;
1184 int parse_remote(string const &, string const &, string &)
1186 run_mode = USE_REMOTE;
1191 int parse_force(string const & arg, string const &, string &)
1194 force_overwrite = ALL_FILES;
1196 } else if (arg == "main") {
1197 force_overwrite = MAIN_FILE;
1199 } else if (arg == "none") {
1200 force_overwrite = NO_FILES;
1203 force_overwrite = ALL_FILES;
1211 void LyX::easyParse(int & argc, char * argv[])
1213 map<string, cmd_helper> cmdmap;
1215 cmdmap["-dbg"] = parse_dbg;
1216 cmdmap["-help"] = parse_help;
1217 cmdmap["--help"] = parse_help;
1218 cmdmap["-version"] = parse_version;
1219 cmdmap["--version"] = parse_version;
1220 cmdmap["-sysdir"] = parse_sysdir;
1221 cmdmap["-userdir"] = parse_userdir;
1222 cmdmap["-x"] = parse_execute;
1223 cmdmap["--execute"] = parse_execute;
1224 cmdmap["-e"] = parse_export;
1225 cmdmap["--export"] = parse_export;
1226 cmdmap["-E"] = parse_export_to;
1227 cmdmap["--export-to"] = parse_export_to;
1228 cmdmap["-i"] = parse_import;
1229 cmdmap["--import"] = parse_import;
1230 cmdmap["-geometry"] = parse_geometry;
1231 cmdmap["-batch"] = parse_batch;
1232 cmdmap["-f"] = parse_force;
1233 cmdmap["--force-overwrite"] = parse_force;
1234 cmdmap["-n"] = parse_noremote;
1235 cmdmap["--no-remote"] = parse_noremote;
1236 cmdmap["-r"] = parse_remote;
1237 cmdmap["--remote"] = parse_remote;
1239 for (int i = 1; i < argc; ++i) {
1240 map<string, cmd_helper>::const_iterator it
1241 = cmdmap.find(argv[i]);
1243 // don't complain if not found - may be parsed later
1244 if (it == cmdmap.end())
1248 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1250 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1253 int const remove = 1 + it->second(arg, arg2, batch);
1255 pimpl_->batch_commands.push_back(batch);
1257 // Now, remove used arguments by shifting
1258 // the following ones remove places down.
1260 os::remove_internal_args(i, remove);
1262 for (int j = i; j < argc; ++j)
1263 argv[j] = argv[j + remove];
1270 FuncStatus getStatus(FuncRequest const & action)
1273 return theApp()->getStatus(action);
1277 void dispatch(FuncRequest const & action)
1280 return theApp()->dispatch(action);
1284 void dispatch(FuncRequest const & action, DispatchResult & dr)
1287 return theApp()->dispatch(action, dr);
1291 vector<string> & theFilesToLoad()
1293 LAPPERR(singleton_);
1294 return singleton_->pimpl_->files_to_load_;
1298 BufferList & theBufferList()
1300 LAPPERR(singleton_);
1301 return singleton_->pimpl_->buffer_list_;
1305 Server & theServer()
1307 // FIXME: this should not be use_gui dependent
1309 LAPPERR(singleton_);
1310 return *singleton_->pimpl_->lyx_server_.get();
1314 ServerSocket & theServerSocket()
1316 // FIXME: this should not be use_gui dependent
1318 LAPPERR(singleton_);
1319 return *singleton_->pimpl_->lyx_socket_.get();
1323 KeyMap & theTopLevelKeymap()
1325 LAPPERR(singleton_);
1326 return singleton_->pimpl_->toplevel_keymap_;
1330 Converters & theConverters()
1332 LAPPERR(singleton_);
1333 return singleton_->pimpl_->converters_;
1337 Converters & theSystemConverters()
1339 LAPPERR(singleton_);
1340 return singleton_->pimpl_->system_converters_;
1344 Movers & theMovers()
1346 LAPPERR(singleton_);
1347 return singleton_->pimpl_->movers_;
1351 Mover const & getMover(string const & fmt)
1353 LAPPERR(singleton_);
1354 return singleton_->pimpl_->movers_(fmt);
1358 void setMover(string const & fmt, string const & command)
1360 LAPPERR(singleton_);
1361 singleton_->pimpl_->movers_.set(fmt, command);
1365 Movers & theSystemMovers()
1367 LAPPERR(singleton_);
1368 return singleton_->pimpl_->system_movers_;
1372 Messages const & getMessages(string const & language)
1374 LAPPERR(singleton_);
1375 return singleton_->messages(language);
1379 Messages const & getGuiMessages()
1381 LAPPERR(singleton_);
1382 // A cache to translate full language name to language code
1383 static string last_language = "auto";
1385 if (lyxrc.gui_language != last_language) {
1386 if (lyxrc.gui_language == "auto")
1389 Language const * l = languages.getLanguage(lyxrc.gui_language);
1390 code = l ? l->code() : string();
1392 last_language = lyxrc.gui_language;
1394 return singleton_->messages(code);
1398 Session & theSession()
1400 LAPPERR(singleton_);
1401 return *singleton_->pimpl_->session_.get();
1405 LaTeXFonts & theLaTeXFonts()
1407 LAPPERR(singleton_);
1408 if (!singleton_->pimpl_->latexfonts_)
1409 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1410 return *singleton_->pimpl_->latexfonts_;
1414 CmdDef & theTopLevelCmdDef()
1416 LAPPERR(singleton_);
1417 return singleton_->pimpl_->toplevel_cmddef_;
1421 SpellChecker * theSpellChecker()
1423 if (!singleton_->pimpl_->spell_checker_)
1425 return singleton_->pimpl_->spell_checker_;
1429 void setSpellChecker()
1431 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1432 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1434 if (lyxrc.spellchecker == "native") {
1435 #if defined(USE_MACOSX_PACKAGING)
1436 if (!singleton_->pimpl_->apple_spell_checker_)
1437 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1438 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1440 singleton_->pimpl_->spell_checker_ = 0;
1442 } else if (lyxrc.spellchecker == "aspell") {
1443 #if defined(USE_ASPELL)
1444 if (!singleton_->pimpl_->aspell_checker_)
1445 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1446 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1448 singleton_->pimpl_->spell_checker_ = 0;
1450 } else if (lyxrc.spellchecker == "enchant") {
1451 #if defined(USE_ENCHANT)
1452 if (!singleton_->pimpl_->enchant_checker_)
1453 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1454 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1456 singleton_->pimpl_->spell_checker_ = 0;
1458 } else if (lyxrc.spellchecker == "hunspell") {
1459 #if defined(USE_HUNSPELL)
1460 if (!singleton_->pimpl_->hunspell_checker_)
1461 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1462 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1464 singleton_->pimpl_->spell_checker_ = 0;
1467 singleton_->pimpl_->spell_checker_ = 0;
1469 if (singleton_->pimpl_->spell_checker_) {
1470 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1471 singleton_->pimpl_->spell_checker_->advanceChangeNumber();