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"
49 #include "frontends/alert.h"
50 #include "frontends/Application.h"
52 #include "support/lassert.h"
53 #include "support/debug.h"
54 #include "support/environment.h"
55 #include "support/ExceptionMessage.h"
56 #include "support/filetools.h"
57 #include "support/gettext.h"
58 #include "support/lstrings.h"
59 #include "support/Messages.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62 #include "support/Path.h"
63 #include "support/Systemcall.h"
65 #include "support/bind.h"
66 #include <boost/scoped_ptr.hpp>
77 using namespace lyx::support;
81 namespace Alert = frontend::Alert;
82 namespace os = support::os;
86 // Are we using the GUI at all? We default to true and this is changed
87 // to false when the export feature is used.
92 // We default to open documents in an already running instance, provided that
93 // the lyxpipe has been setup. This can be overridden either on the command
94 // line or through preference settings.
96 RunMode run_mode = PREFERRED;
99 // Tell what files can be silently overwritten during batch export.
100 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
101 // Unless specified on command line (through the -f switch) or through the
102 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
104 OverwriteFiles force_overwrite = UNSPECIFIED;
109 // Filled with the command line arguments "foo" of "-sysdir foo" or
111 string cl_system_support;
112 string cl_user_support;
116 LyX * singleton_ = 0;
118 void showFileError(string const & error)
120 Alert::warning(_("Could not read configuration file"),
121 bformat(_("Error while reading the configuration file\n%1$s.\n"
122 "Please check your installation."), from_utf8(error)));
126 void reconfigureUserLyXDir()
128 string const configure_command = package().configure_command();
130 lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
131 PathChanger p(package().user_support());
133 one.startscript(Systemcall::Wait, configure_command);
134 lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
139 /// The main application class private implementation.
142 Impl() : spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
144 // Set the default User Interface language as soon as possible.
145 // The language used will be derived from the environment
147 messages_["GUI"] = Messages();
152 delete apple_spell_checker_;
153 delete aspell_checker_;
154 delete enchant_checker_;
155 delete hunspell_checker_;
159 BufferList buffer_list_;
161 KeyMap toplevel_keymap_;
163 CmdDef toplevel_cmddef_;
165 boost::scoped_ptr<Server> lyx_server_;
167 boost::scoped_ptr<ServerSocket> lyx_socket_;
169 boost::scoped_ptr<frontend::Application> application_;
170 /// lyx session, containing lastfiles, lastfilepos, and lastopened
171 boost::scoped_ptr<Session> session_;
173 /// Files to load at start.
174 vector<string> files_to_load_;
176 /// The messages translators.
177 map<string, Messages> messages_;
179 /// The file converters.
180 Converters converters_;
182 // The system converters copy after reading lyxrc.defaults.
183 Converters system_converters_;
188 Movers system_movers_;
190 /// has this user started lyx for the first time?
192 /// the parsed command line batch command if any
193 vector<string> batch_commands;
196 SpellChecker * spell_checker_;
198 SpellChecker * apple_spell_checker_;
200 SpellChecker * aspell_checker_;
202 SpellChecker * enchant_checker_;
204 SpellChecker * hunspell_checker_;
208 frontend::Application * theApp()
211 return singleton_->pimpl_->application_.get();
221 WordList::cleanupWordLists();
225 void lyx_exit(int exit_code)
228 // Something wrong happened so better save everything, just in
233 // Properly crash in debug mode in order to get a useful backtrace.
237 // In release mode, try to exit gracefully.
239 theApp()->exit(exit_code);
253 Messages & LyX::messages(string const & language)
255 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
257 if (it != pimpl_->messages_.end())
260 pair<map<string, Messages>::iterator, bool> result =
261 pimpl_->messages_.insert(make_pair(language, Messages(language)));
263 LASSERT(result.second, /**/);
264 return result.first->second;
268 void setRcGuiLanguage()
270 LASSERT(singleton_, /**/);
271 if (lyxrc.gui_language == "auto")
273 Language const * language = languages.getLanguage(lyxrc.gui_language);
275 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << language->code());
276 if (!setEnv("LANGUAGE", language->code()))
277 LYXERR(Debug::LOCALE, "\t... failed!");
279 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
280 if (!setEnv("LC_ALL", "en_US"))
281 LYXERR(Debug::LOCALE, "\t... failed!");
283 singleton_->pimpl_->messages_["GUI"] = Messages();
287 int LyX::exec(int & argc, char * argv[])
289 // Minimal setting of locale before parsing command line
291 init_package(os::utf8_argv(0), string(), string());
292 // we do not get to this point when init_package throws an exception
294 } catch (ExceptionMessage const & message) {
295 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
298 // Here we need to parse the command line. At least
299 // we need to parse for "-dbg" and "-help"
300 easyParse(argc, argv);
303 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
304 } catch (ExceptionMessage const & message) {
305 if (message.type_ == ErrorException) {
306 Alert::error(message.title_, message.details_);
308 } else if (message.type_ == WarningException) {
309 Alert::warning(message.title_, message.details_);
313 // Reinit the messages machinery in case package() knows
314 // something interesting about the locale directory.
318 // FIXME: create a ConsoleApplication
319 int exit_status = init(argc, argv);
325 // this is correct, since return values are inverted.
326 exit_status = !loadFiles();
328 if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
333 BufferList::iterator begin = pimpl_->buffer_list_.begin();
335 bool final_success = false;
336 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
338 if (buf != buf->masterBuffer())
340 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
341 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
343 for (; bcit != bcend; bcit++) {
344 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
345 buf->dispatch(*bcit, dr);
346 final_success |= !dr.error();
350 return !final_success;
353 // Let the frontend parse and remove all arguments that it knows
354 pimpl_->application_.reset(createApplication(argc, argv));
356 // Reestablish our defaults, as Qt overwrites them
357 // after createApplication()
360 // Parse and remove all known arguments in the LyX singleton
361 // Give an error for all remaining ones.
362 int exit_status = init(argc, argv);
364 // Kill the application object before exiting.
365 pimpl_->application_.reset();
371 // If not otherwise specified by a command line option or
372 // by preferences, we default to reuse a running instance.
373 if (run_mode == PREFERRED)
374 run_mode = USE_REMOTE;
377 /* Create a CoreApplication class that will provide the main event loop
378 * and the socket callback registering. With Qt4, only QtCore
379 * library would be needed.
380 * When this is done, a server_mode could be created and the following two
381 * line would be moved out from here.
382 * However, note that the first of the two lines below triggers the
383 * "single instance" behavior, which should occur right at this point.
385 // Note: socket callback must be registered after init(argc, argv)
386 // such that package().temp_dir() is properly initialized.
387 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
388 pimpl_->lyx_socket_.reset(new ServerSocket(
389 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
391 // Start the real execution loop.
392 if (!theServer().deferredLoadingToOtherInstance())
393 exit_status = pimpl_->application_->exec();
394 else if (!pimpl_->files_to_load_.empty()) {
395 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
396 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
397 lyxerr << _("The following files could not be loaded:") << endl;
398 for (; it != end; ++it)
399 lyxerr << *it << endl;
408 void LyX::prepareExit()
410 // Clear the clipboard and selection stack:
411 cap::clearCutStack();
412 cap::clearSelection();
414 // Write the index file of the converter cache
415 ConverterCache::get().writeIndex();
417 // close buffers first
418 pimpl_->buffer_list_.closeAll();
420 // register session changes and shutdown server and socket
422 if (pimpl_->session_)
423 pimpl_->session_->writeFile();
424 pimpl_->session_.reset();
425 pimpl_->lyx_server_.reset();
426 pimpl_->lyx_socket_.reset();
429 // do any other cleanup procedures now
430 if (package().temp_dir() != package().system_temp_dir()) {
431 string const abs_tmpdir = package().temp_dir().absFileName();
432 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
433 docstring const msg =
434 bformat(_("%1$s does not appear like a LyX created temporary directory."),
435 from_utf8(abs_tmpdir));
436 Alert::warning(_("Cannot remove temporary directory"), msg);
438 LYXERR(Debug::INFO, "Deleting tmp dir "
439 << package().temp_dir().absFileName());
440 if (!package().temp_dir().destroyDirectory()) {
441 docstring const msg =
442 bformat(_("Unable to remove the temporary directory %1$s"),
443 from_utf8(package().temp_dir().absFileName()));
444 Alert::warning(_("Unable to remove temporary directory"), msg);
449 // Kill the application object before exiting. This avoids crashes
450 // when exiting on Linux.
451 pimpl_->application_.reset();
455 void LyX::earlyExit(int status)
457 LASSERT(pimpl_->application_.get(), /**/);
458 // LyX::pimpl_::application_ is not initialised at this
459 // point so it's safe to just exit after some cleanup.
465 int LyX::init(int & argc, char * argv[])
467 // check for any spurious extra arguments
468 // other than documents
469 for (int argi = 1; argi < argc ; ++argi) {
470 if (argv[argi][0] == '-') {
472 bformat(_("Wrong command line option `%1$s'. Exiting."),
473 from_utf8(os::utf8_argv(argi)))) << endl;
478 // Initialization of LyX (reads lyxrc and more)
479 LYXERR(Debug::INIT, "Initializing LyX::init...");
480 bool success = init();
481 LYXERR(Debug::INIT, "Initializing LyX::init...done");
485 // Remaining arguments are assumed to be files to load.
486 for (int argi = 1; argi < argc; ++argi)
487 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
490 pimpl_->files_to_load_.push_back(
491 i18nLibFileSearch("examples", "splash.lyx").absFileName());
498 bool LyX::loadFiles()
500 LASSERT(!use_gui, /**/);
502 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
503 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
505 for (; it != end; ++it) {
506 // get absolute path of file and add ".lyx" to
507 // the filename if necessary
508 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
514 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName(), false);
515 if (buf->loadLyXFile() == Buffer::ReadSuccess) {
516 ErrorList const & el = buf->errorList("Parse");
518 for_each(el.begin(), el.end(),
519 bind(&LyX::printError, this, _1));
522 pimpl_->buffer_list_.release(buf);
530 void execBatchCommands()
532 LASSERT(singleton_, /**/);
533 singleton_->execCommands();
537 void LyX::execCommands()
539 // The advantage of doing this here is that the event loop
540 // is already started. So any need for interaction will be
543 // if reconfiguration is needed.
544 if (LayoutFileList::get().empty()) {
545 switch (Alert::prompt(
546 _("No textclass is found"),
547 _("LyX will only have minimal functionality because no textclasses "
548 "have been found. You can either try to reconfigure LyX normally, "
549 "try to reconfigure without checking your LaTeX installation, or continue."),
556 // regular reconfigure
557 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
560 // reconfigure --without-latex-config
561 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
562 " --without-latex-config"));
569 // create the first main window
570 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
572 if (!pimpl_->files_to_load_.empty()) {
573 // if some files were specified at command-line we assume that the
574 // user wants to edit *these* files and not to restore the session.
575 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
577 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
579 // clear this list to save a few bytes of RAM
580 pimpl_->files_to_load_.clear();
582 pimpl_->application_->restoreGuiSession();
584 // Execute batch commands if available
585 if (pimpl_->batch_commands.empty())
588 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
589 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
590 for (; bcit != bcend; bcit++) {
591 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
592 lyx::dispatch(lyxaction.lookupFunc(*bcit));
600 The SIGHUP signal does not exist on Windows and does not need to be handled.
602 Windows handles SIGFPE and SIGSEGV signals as expected.
604 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
605 cause a new thread to be spawned. This may well result in unexpected
606 behaviour by the single-threaded LyX.
608 SIGTERM signals will come only from another process actually sending
609 that signal using 'raise' in Windows' POSIX compatability layer. It will
610 not come from the general "terminate process" methods that everyone
611 actually uses (and which can't be trapped). Killing an app 'politely' on
612 Windows involves first sending a WM_CLOSE message, something that is
613 caught already by the Qt frontend.
615 For more information see:
617 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
618 ...signals are mostly useless on Windows for a variety of reasons that are
621 'UNIX Application Migration Guide, Chapter 9'
622 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
624 'How To Terminate an Application "Cleanly" in Win32'
625 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
629 static void error_handler(int err_sig)
631 // Throw away any signals other than the first one received.
632 static sig_atomic_t handling_error = false;
635 handling_error = true;
637 // We have received a signal indicating a fatal error, so
638 // try and save the data ASAP.
641 // These lyxerr calls may or may not work:
643 // Signals are asynchronous, so the main program may be in a very
644 // fragile state when a signal is processed and thus while a signal
645 // handler function executes.
646 // In general, therefore, we should avoid performing any
647 // I/O operations or calling most library and system functions from
650 // This shouldn't matter here, however, as we've already invoked
656 msg = _("SIGHUP signal caught!\nBye.");
660 msg = _("SIGFPE signal caught!\nBye.");
663 msg = _("SIGSEGV signal caught!\n"
664 "Sorry, you have found a bug in LyX, "
665 "hope you have not lost any data.\n"
666 "Please read the bug-reporting instructions "
667 "in 'Help->Introduction' and send us a bug report, "
668 "if necessary. Thanks!\nBye.");
677 lyxerr << "\nlyx: " << msg << endl;
678 // try to make a GUI message
679 Alert::error(_("LyX crashed!"), msg);
682 // Deinstall the signal handlers
684 signal(SIGHUP, SIG_DFL);
686 signal(SIGINT, SIG_DFL);
687 signal(SIGFPE, SIG_DFL);
688 signal(SIGSEGV, SIG_DFL);
689 signal(SIGTERM, SIG_DFL);
692 if (err_sig == SIGSEGV ||
693 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
695 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
698 // with abort() it crashes again.
711 void LyX::printError(ErrorItem const & ei)
713 docstring tmp = _("LyX: ") + ei.error + char_type(':')
715 cerr << to_utf8(tmp) << endl;
722 signal(SIGHUP, error_handler);
724 signal(SIGFPE, error_handler);
725 signal(SIGSEGV, error_handler);
726 signal(SIGINT, error_handler);
727 signal(SIGTERM, error_handler);
728 // SIGPIPE can be safely ignored.
730 lyxrc.tempdir_path = package().temp_dir().absFileName();
731 lyxrc.document_path = package().document_dir().absFileName();
733 if (lyxrc.example_path.empty()) {
734 lyxrc.example_path = addPath(package().system_support().absFileName(),
737 if (lyxrc.template_path.empty()) {
738 lyxrc.template_path = addPath(package().system_support().absFileName(),
742 // init LyXDir environment variable
743 string const lyx_dir = package().lyx_dir().absFileName();
744 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
745 if (!setEnv("LyXDir", lyx_dir))
746 LYXERR(Debug::INIT, "\t... failed!");
749 // Read configuration files
752 // This one may have been distributed along with LyX.
753 if (!readRcFile("lyxrc.dist"))
756 // Set the language defined by the distributor.
759 // Set the PATH correctly.
760 #if !defined (USE_POSIX_PACKAGING)
761 // Add the directory containing the LyX executable to the path
762 // so that LyX can find things like tex2lyx.
763 if (package().build_support().empty())
764 prependEnvPath("PATH", package().binary_dir().absFileName());
766 if (!lyxrc.path_prefix.empty())
767 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
769 // Check that user LyX directory is ok.
770 if (queryUserLyXDir(package().explicit_user_support()))
771 reconfigureUserLyXDir();
774 // No need for a splash when there is no GUI
776 // Default is to overwrite the main file during export, unless
777 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
778 if (force_overwrite == UNSPECIFIED) {
779 string const what = getEnv("LYX_FORCE_OVERWRITE");
781 force_overwrite = ALL_FILES;
782 else if (what == "none")
783 force_overwrite = NO_FILES;
785 force_overwrite = MAIN_FILE;
789 // This one is generated in user_support directory by lib/configure.py.
790 if (!readRcFile("lyxrc.defaults"))
793 // Query the OS to know what formats are viewed natively
794 formats.setAutoOpen();
796 // Read lyxrc.dist again to be able to override viewer auto-detection.
797 readRcFile("lyxrc.dist");
799 // Set again the language defined by the distributor.
802 system_lyxrc = lyxrc;
803 system_formats = formats;
804 pimpl_->system_converters_ = pimpl_->converters_;
805 pimpl_->system_movers_ = pimpl_->movers_;
806 system_lcolor = lcolor;
808 // This one is edited through the preferences dialog.
809 if (!readRcFile("preferences", true))
812 if (!readEncodingsFile("encodings", "unicodesymbols"))
814 if (!readLanguagesFile("languages"))
817 // Set the language defined by the user.
820 LYXERR(Debug::INIT, "Reading layouts...");
822 LayoutFileList::get().read();
824 theModuleList.read();
826 // read keymap and ui files in batch mode as well
827 // because InsetInfo needs to know these to produce
828 // the correct output
830 // Set up command definitions
831 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
835 pimpl_->toplevel_keymap_.read("site");
836 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
837 // load user bind file user.bind
838 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
840 if (lyxerr.debugging(Debug::LYXRC))
843 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
844 if (!lyxrc.path_prefix.empty())
845 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
847 FileName const document_path(lyxrc.document_path);
848 if (document_path.exists() && document_path.isDirectory())
849 package().document_dir() = document_path;
851 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
852 if (package().temp_dir().empty()) {
853 Alert::error(_("Could not create temporary directory"),
854 bformat(_("Could not create a temporary directory in\n"
856 "Make sure that this path exists and is writable and try again."),
857 from_utf8(lyxrc.tempdir_path)));
858 // createLyXTmpDir() tries sufficiently hard to create a
859 // usable temp dir, so the probability to come here is
860 // close to zero. We therefore don't try to overcome this
861 // problem with e.g. asking the user for a new path and
862 // trying again but simply exit.
866 LYXERR(Debug::INIT, "LyX tmp dir: `"
867 << package().temp_dir().absFileName() << '\'');
869 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
870 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
872 // This must happen after package initialization and after lyxrc is
873 // read, therefore it can't be done by a static object.
874 ConverterCache::init();
880 void emergencyCleanup()
882 // what to do about tmpfiles is non-obvious. we would
883 // like to delete any we find, but our lyxdir might
884 // contain documents etc. which might be helpful on
887 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
889 if (singleton_->pimpl_->lyx_server_)
890 singleton_->pimpl_->lyx_server_->emergencyCleanup();
891 singleton_->pimpl_->lyx_server_.reset();
892 singleton_->pimpl_->lyx_socket_.reset();
897 // return true if file does not exist or is older than configure.py.
898 static bool needsUpdate(string const & file)
900 // We cannot initialize configure_script directly because the package
901 // is not initialized yet when static objects are constructed.
902 static FileName configure_script;
903 static bool firstrun = true;
906 FileName(addName(package().system_support().absFileName(),
912 FileName(addName(package().user_support().absFileName(), file));
913 return !absfile.exists()
914 || configure_script.lastModified() > absfile.lastModified();
918 bool LyX::queryUserLyXDir(bool explicit_userdir)
920 // Does user directory exist?
921 FileName const sup = package().user_support();
922 if (sup.exists() && sup.isDirectory()) {
925 return needsUpdate("lyxrc.defaults")
926 || needsUpdate("lyxmodules.lst")
927 || needsUpdate("textclass.lst")
928 || needsUpdate("packages.lst");
931 first_start = !explicit_userdir;
933 // If the user specified explicitly a directory, ask whether
934 // to create it. If the user says "no", then exit.
935 if (explicit_userdir &&
937 _("Missing user LyX directory"),
938 bformat(_("You have specified a non-existent user "
939 "LyX directory, %1$s.\n"
940 "It is needed to keep your own configuration."),
941 from_utf8(package().user_support().absFileName())),
943 _("&Create directory"),
945 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
946 earlyExit(EXIT_FAILURE);
949 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
950 from_utf8(sup.absFileName()))) << endl;
952 if (!sup.createDirectory(0755)) {
953 // Failed, so let's exit.
954 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
956 earlyExit(EXIT_FAILURE);
963 bool LyX::readRcFile(string const & name, bool check_format)
965 LYXERR(Debug::INIT, "About to read " << name << "... ");
967 FileName const lyxrc_path = libFileSearch(string(), name);
968 if (lyxrc_path.empty()) {
969 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
971 // This was the previous logic, but can it be right??
974 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
975 bool const success = lyxrc.read(lyxrc_path, check_format);
981 // Read the languages file `name'
982 bool LyX::readLanguagesFile(string const & name)
984 LYXERR(Debug::INIT, "About to read " << name << "...");
986 FileName const lang_path = libFileSearch(string(), name);
987 if (lang_path.empty()) {
991 languages.read(lang_path);
996 // Read the encodings file `name'
997 bool LyX::readEncodingsFile(string const & enc_name,
998 string const & symbols_name)
1000 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1001 << symbols_name << "...");
1003 FileName const symbols_path = libFileSearch(string(), symbols_name);
1004 if (symbols_path.empty()) {
1005 showFileError(symbols_name);
1009 FileName const enc_path = libFileSearch(string(), enc_name);
1010 if (enc_path.empty()) {
1011 showFileError(enc_name);
1014 encodings.read(enc_path, symbols_path);
1021 /// return the the number of arguments consumed
1022 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
1024 int parse_dbg(string const & arg, string const &, string &)
1027 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1028 Debug::showTags(lyxerr);
1031 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1033 lyxerr.setLevel(Debug::value(arg));
1034 Debug::showLevel(lyxerr, lyxerr.level());
1039 int parse_help(string const &, string const &, string &)
1042 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1043 "Command line switches (case sensitive):\n"
1044 "\t-help summarize LyX usage\n"
1045 "\t-userdir dir set user directory to dir\n"
1046 "\t-sysdir dir set system directory to dir\n"
1047 "\t-geometry WxH+X+Y set geometry of the main window\n"
1048 "\t-dbg feature[,feature]...\n"
1049 " select the features to debug.\n"
1050 " Type `lyx -dbg' to see the list of features\n"
1051 "\t-x [--execute] command\n"
1052 " where command is a lyx command.\n"
1053 "\t-e [--export] fmt\n"
1054 " where fmt is the export format of choice.\n"
1055 " Look on Tools->Preferences->File formats->Format\n"
1056 " to get an idea which parameters should be passed.\n"
1057 " Note that the order of -e and -x switches matters.\n"
1058 "\t-E [--export-to] fmt filename\n"
1059 " where fmt is the export format of choice (see --export),\n"
1060 " and filename is the destination filename.\n"
1061 "\t-i [--import] fmt file.xxx\n"
1062 " where fmt is the import format of choice\n"
1063 " and file.xxx is the file to be imported.\n"
1064 "\t-f [--force-overwrite] what\n"
1065 " where what is either `all', `main' or `none',\n"
1066 " specifying whether all files, main file only, or no files,\n"
1067 " respectively, are to be overwritten during a batch export.\n"
1068 " Anything else is equivalent to `all', but is not consumed.\n"
1069 "\t-n [--no-remote]\n"
1070 " open documents in a new instance\n"
1072 " open documents in an already running instance\n"
1073 " (a working lyxpipe is needed)\n"
1074 "\t-batch execute commands without launching GUI and exit.\n"
1075 "\t-version summarize version and build info\n"
1076 "Check the LyX man page for more details.")) << endl;
1082 int parse_version(string const &, string const &, string &)
1084 lyxerr << "LyX " << lyx_version
1085 << " (" << lyx_release_date << ")" << endl;
1086 lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1088 lyxerr << lyx_version_info << endl;
1094 int parse_sysdir(string const & arg, string const &, string &)
1097 Alert::error(_("No system directory"),
1098 _("Missing directory for -sysdir switch"));
1101 cl_system_support = arg;
1106 int parse_userdir(string const & arg, string const &, string &)
1109 Alert::error(_("No user directory"),
1110 _("Missing directory for -userdir switch"));
1113 cl_user_support = arg;
1118 int parse_execute(string const & arg, string const &, string & batch)
1121 Alert::error(_("Incomplete command"),
1122 _("Missing command string after --execute switch"));
1130 int parse_export_to(string const & type, string const & output_file, string & batch)
1133 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1134 "--export-to switch")) << endl;
1137 if (output_file.empty()) {
1138 lyxerr << to_utf8(_("Missing destination filename after "
1139 "--export-to switch")) << endl;
1142 batch = "buffer-export " + type + " " + output_file;
1148 int parse_export(string const & type, string const &, string & batch)
1151 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1152 "--export switch")) << endl;
1155 batch = "buffer-export " + type;
1161 int parse_import(string const & type, string const & file, string & batch)
1164 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1165 "--import switch")) << endl;
1169 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1173 batch = "buffer-import " + type + ' ' + file;
1178 int parse_geometry(string const & arg1, string const &, string &)
1181 // don't remove "-geometry", it will be pruned out later in the
1182 // frontend if need be.
1187 int parse_batch(string const &, string const &, string &)
1194 int parse_noremote(string const &, string const &, string &)
1196 run_mode = NEW_INSTANCE;
1201 int parse_remote(string const &, string const &, string &)
1203 run_mode = USE_REMOTE;
1208 int parse_force(string const & arg, string const &, string &)
1211 force_overwrite = ALL_FILES;
1213 } else if (arg == "main") {
1214 force_overwrite = MAIN_FILE;
1216 } else if (arg == "none") {
1217 force_overwrite = NO_FILES;
1220 force_overwrite = ALL_FILES;
1228 void LyX::easyParse(int & argc, char * argv[])
1230 map<string, cmd_helper> cmdmap;
1232 cmdmap["-dbg"] = parse_dbg;
1233 cmdmap["-help"] = parse_help;
1234 cmdmap["--help"] = parse_help;
1235 cmdmap["-version"] = parse_version;
1236 cmdmap["--version"] = parse_version;
1237 cmdmap["-sysdir"] = parse_sysdir;
1238 cmdmap["-userdir"] = parse_userdir;
1239 cmdmap["-x"] = parse_execute;
1240 cmdmap["--execute"] = parse_execute;
1241 cmdmap["-e"] = parse_export;
1242 cmdmap["--export"] = parse_export;
1243 cmdmap["-E"] = parse_export_to;
1244 cmdmap["--export-to"] = parse_export_to;
1245 cmdmap["-i"] = parse_import;
1246 cmdmap["--import"] = parse_import;
1247 cmdmap["-geometry"] = parse_geometry;
1248 cmdmap["-batch"] = parse_batch;
1249 cmdmap["-f"] = parse_force;
1250 cmdmap["--force-overwrite"] = parse_force;
1251 cmdmap["-n"] = parse_noremote;
1252 cmdmap["--no-remote"] = parse_noremote;
1253 cmdmap["-r"] = parse_remote;
1254 cmdmap["--remote"] = parse_remote;
1256 for (int i = 1; i < argc; ++i) {
1257 map<string, cmd_helper>::const_iterator it
1258 = cmdmap.find(argv[i]);
1260 // don't complain if not found - may be parsed later
1261 if (it == cmdmap.end())
1265 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1267 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1270 int const remove = 1 + it->second(arg, arg2, batch);
1272 pimpl_->batch_commands.push_back(batch);
1274 // Now, remove used arguments by shifting
1275 // the following ones remove places down.
1277 os::remove_internal_args(i, remove);
1279 for (int j = i; j < argc; ++j)
1280 argv[j] = argv[j + remove];
1287 FuncStatus getStatus(FuncRequest const & action)
1289 LASSERT(theApp(), /**/);
1290 return theApp()->getStatus(action);
1294 void dispatch(FuncRequest const & action)
1296 LASSERT(theApp(), /**/);
1297 return theApp()->dispatch(action);
1301 void dispatch(FuncRequest const & action, DispatchResult & dr)
1303 LASSERT(theApp(), /**/);
1304 return theApp()->dispatch(action, dr);
1308 vector<string> & theFilesToLoad()
1310 LASSERT(singleton_, /**/);
1311 return singleton_->pimpl_->files_to_load_;
1315 BufferList & theBufferList()
1317 LASSERT(singleton_, /**/);
1318 return singleton_->pimpl_->buffer_list_;
1322 Server & theServer()
1324 // FIXME: this should not be use_gui dependent
1325 LASSERT(use_gui, /**/);
1326 LASSERT(singleton_, /**/);
1327 return *singleton_->pimpl_->lyx_server_.get();
1331 ServerSocket & theServerSocket()
1333 // FIXME: this should not be use_gui dependent
1334 LASSERT(use_gui, /**/);
1335 LASSERT(singleton_, /**/);
1336 return *singleton_->pimpl_->lyx_socket_.get();
1340 KeyMap & theTopLevelKeymap()
1342 LASSERT(singleton_, /**/);
1343 return singleton_->pimpl_->toplevel_keymap_;
1347 Converters & theConverters()
1349 LASSERT(singleton_, /**/);
1350 return singleton_->pimpl_->converters_;
1354 Converters & theSystemConverters()
1356 LASSERT(singleton_, /**/);
1357 return singleton_->pimpl_->system_converters_;
1361 Movers & theMovers()
1363 LASSERT(singleton_, /**/);
1364 return singleton_->pimpl_->movers_;
1368 Mover const & getMover(string const & fmt)
1370 LASSERT(singleton_, /**/);
1371 return singleton_->pimpl_->movers_(fmt);
1375 void setMover(string const & fmt, string const & command)
1377 LASSERT(singleton_, /**/);
1378 singleton_->pimpl_->movers_.set(fmt, command);
1382 Movers & theSystemMovers()
1384 LASSERT(singleton_, /**/);
1385 return singleton_->pimpl_->system_movers_;
1389 Messages const & getMessages(string const & language)
1391 LASSERT(singleton_, /**/);
1392 return singleton_->messages(language);
1396 Messages const & getGuiMessages()
1398 LASSERT(singleton_, /**/);
1399 return singleton_->pimpl_->messages_["GUI"];
1403 Session & theSession()
1405 LASSERT(singleton_, /**/);
1406 return *singleton_->pimpl_->session_.get();
1410 CmdDef & theTopLevelCmdDef()
1412 LASSERT(singleton_, /**/);
1413 return singleton_->pimpl_->toplevel_cmddef_;
1417 SpellChecker * theSpellChecker()
1419 if (!singleton_->pimpl_->spell_checker_)
1421 return singleton_->pimpl_->spell_checker_;
1425 void setSpellChecker()
1427 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1428 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1430 if (lyxrc.spellchecker == "native") {
1431 #if defined(USE_MACOSX_PACKAGING)
1432 if (!singleton_->pimpl_->apple_spell_checker_)
1433 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker();
1434 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1436 singleton_->pimpl_->spell_checker_ = 0;
1438 } else if (lyxrc.spellchecker == "aspell") {
1439 #if defined(USE_ASPELL)
1440 if (!singleton_->pimpl_->aspell_checker_)
1441 singleton_->pimpl_->aspell_checker_ = new AspellChecker();
1442 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1444 singleton_->pimpl_->spell_checker_ = 0;
1446 } else if (lyxrc.spellchecker == "enchant") {
1447 #if defined(USE_ENCHANT)
1448 if (!singleton_->pimpl_->enchant_checker_)
1449 singleton_->pimpl_->enchant_checker_ = new EnchantChecker();
1450 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1452 singleton_->pimpl_->spell_checker_ = 0;
1454 } else if (lyxrc.spellchecker == "hunspell") {
1455 #if defined(USE_HUNSPELL)
1456 if (!singleton_->pimpl_->hunspell_checker_)
1457 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker();
1458 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1460 singleton_->pimpl_->spell_checker_ = 0;
1463 singleton_->pimpl_->spell_checker_ = 0;
1465 if (singleton_->pimpl_->spell_checker_) {
1466 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1467 singleton_->pimpl_->spell_checker_->advanceChangeNumber();