]> git.lyx.org Git - features.git/blob - src/LyX.cpp
tex2lyx: Run configure.py if needed (bug #8539)
[features.git] / src / LyX.cpp
1 /**
2  * \file LyX.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16 #include <version.h>
17
18 #include "LyX.h"
19
20 #include "AppleSpellChecker.h"
21 #include "AspellChecker.h"
22 #include "Buffer.h"
23 #include "BufferList.h"
24 #include "CmdDef.h"
25 #include "ColorSet.h"
26 #include "ConverterCache.h"
27 #include "Converter.h"
28 #include "CutAndPaste.h"
29 #include "EnchantChecker.h"
30 #include "Encoding.h"
31 #include "ErrorList.h"
32 #include "Format.h"
33 #include "FuncStatus.h"
34 #include "HunspellChecker.h"
35 #include "KeyMap.h"
36 #include "Language.h"
37 #include "LaTeXFonts.h"
38 #include "LayoutFile.h"
39 #include "Lexer.h"
40 #include "LyX.h"
41 #include "LyXAction.h"
42 #include "LyXRC.h"
43 #include "ModuleList.h"
44 #include "Mover.h"
45 #include "Server.h"
46 #include "ServerSocket.h"
47 #include "Session.h"
48 #include "WordList.h"
49
50 #include "frontends/alert.h"
51 #include "frontends/Application.h"
52
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
64 #include "support/bind.h"
65 #include <boost/scoped_ptr.hpp>
66
67 #include <algorithm>
68 #include <iostream>
69 #include <csignal>
70 #include <map>
71 #include <stdlib.h>
72 #include <string>
73 #include <vector>
74
75 using namespace std;
76 using namespace lyx::support;
77
78 #if defined (USE_MACOSX_PACKAGING)
79 #include <crt_externs.h>
80 #endif
81
82 namespace lyx {
83
84 namespace Alert = frontend::Alert;
85 namespace os = support::os;
86
87
88
89 // Are we using the GUI at all?  We default to true and this is changed
90 // to false when the export feature is used.
91
92 bool use_gui = true;
93
94
95 // We default to open documents in an already running instance, provided that
96 // the lyxpipe has been setup. This can be overridden either on the command
97 // line or through preference settings.
98
99 RunMode run_mode = PREFERRED;
100
101
102 // Tell what files can be silently overwritten during batch export.
103 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
104 // Unless specified on command line (through the -f switch) or through the
105 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
106
107 OverwriteFiles force_overwrite = UNSPECIFIED;
108
109
110 namespace {
111
112 // Filled with the command line arguments "foo" of "-sysdir foo" or
113 // "-userdir foo".
114 string cl_system_support;
115 string cl_user_support;
116
117 string geometryArg;
118
119 LyX * singleton_ = 0;
120
121 void showFileError(string const & error)
122 {
123         Alert::warning(_("Could not read configuration file"),
124                        bformat(_("Error while reading the configuration file\n%1$s.\n"
125                            "Please check your installation."), from_utf8(error)));
126 }
127
128 } // namespace anon
129
130 /// The main application class private implementation.
131 struct LyX::Impl {
132         Impl()
133                 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
134         {}
135
136         ~Impl()
137         {
138                 delete latexfonts_;
139                 delete apple_spell_checker_;
140                 delete aspell_checker_;
141                 delete enchant_checker_;
142                 delete hunspell_checker_;
143         }
144
145         ///
146         BufferList buffer_list_;
147         ///
148         KeyMap toplevel_keymap_;
149         ///
150         CmdDef toplevel_cmddef_;
151         ///
152         boost::scoped_ptr<Server> lyx_server_;
153         ///
154         boost::scoped_ptr<ServerSocket> lyx_socket_;
155         ///
156         boost::scoped_ptr<frontend::Application> application_;
157         /// lyx session, containing lastfiles, lastfilepos, and lastopened
158         boost::scoped_ptr<Session> session_;
159
160         /// Files to load at start.
161         vector<string> files_to_load_;
162
163         /// The messages translators.
164         map<string, Messages> messages_;
165
166         /// The file converters.
167         Converters converters_;
168
169         // The system converters copy after reading lyxrc.defaults.
170         Converters system_converters_;
171
172         ///
173         Movers movers_;
174         ///
175         Movers system_movers_;
176
177         /// has this user started lyx for the first time?
178         bool first_start;
179         /// the parsed command line batch command if any
180         vector<string> batch_commands;
181
182         ///
183         LaTeXFonts * latexfonts_;
184
185         ///
186         SpellChecker * spell_checker_;
187         ///
188         SpellChecker * apple_spell_checker_;
189         ///
190         SpellChecker * aspell_checker_;
191         ///
192         SpellChecker * enchant_checker_;
193         ///
194         SpellChecker * hunspell_checker_;
195 };
196
197
198 ///
199 frontend::Application * theApp()
200 {
201         if (singleton_)
202                 return singleton_->pimpl_->application_.get();
203         else
204                 return 0;
205 }
206
207
208 LyX::~LyX()
209 {
210         delete pimpl_;
211         singleton_ = 0;
212         WordList::cleanupWordLists();
213 }
214
215
216 void lyx_exit(int exit_code)
217 {
218         if (exit_code)
219                 // Something wrong happened so better save everything, just in
220                 // case.
221                 emergencyCleanup();
222
223 #ifndef NDEBUG
224         // Properly crash in debug mode in order to get a useful backtrace.
225         abort();
226 #endif
227
228         // In release mode, try to exit gracefully.
229         if (theApp())
230                 theApp()->exit(exit_code);
231         else
232                 exit(exit_code);
233 }
234
235
236 LyX::LyX()
237         : first_start(false)
238 {
239         singleton_ = this;
240         pimpl_ = new Impl;
241 }
242
243
244 Messages & LyX::messages(string const & language)
245 {
246         map<string, Messages>::iterator it = pimpl_->messages_.find(language);
247
248         if (it != pimpl_->messages_.end())
249                 return it->second;
250
251         pair<map<string, Messages>::iterator, bool> result =
252                         pimpl_->messages_.insert(make_pair(language, Messages(language)));
253
254         LATTEST(result.second);
255         return result.first->second;
256 }
257
258
259 int LyX::exec(int & argc, char * argv[])
260 {
261         // Minimal setting of locale before parsing command line
262         try {
263                 init_package(os::utf8_argv(0), string(), string());
264                 // we do not get to this point when init_package throws an exception
265                 setLocale();
266         } catch (ExceptionMessage const & message) {
267                 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
268         }
269
270         // Here we need to parse the command line. At least
271         // we need to parse for "-dbg" and "-help"
272         easyParse(argc, argv);
273
274         try {
275                 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
276         } catch (ExceptionMessage const & message) {
277                 if (message.type_ == ErrorException) {
278                         Alert::error(message.title_, message.details_);
279                         lyx_exit(1);
280                 } else if (message.type_ == WarningException) {
281                         Alert::warning(message.title_, message.details_);
282                 }
283         }
284
285         // Reinit the messages machinery in case package() knows
286         // something interesting about the locale directory.
287         setLocale();
288
289         if (!use_gui) {
290                 // FIXME: create a ConsoleApplication
291                 int exit_status = init(argc, argv);
292                 if (exit_status) {
293                         prepareExit();
294                         return exit_status;
295                 }
296
297                 // this is correct, since return values are inverted.
298                 exit_status = !loadFiles();
299
300                 if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
301                         prepareExit();
302                         return exit_status;
303                 }
304
305                 BufferList::iterator begin = pimpl_->buffer_list_.begin();
306
307                 bool final_success = false;
308                 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
309                         Buffer * buf = *I;
310                         if (buf != buf->masterBuffer())
311                                 continue;
312                         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
313                         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
314                         DispatchResult dr;
315                         for (; bcit != bcend; ++bcit) {
316                                 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
317                                 buf->dispatch(*bcit, dr);
318                                 final_success |= !dr.error();
319                         }
320                 }
321                 prepareExit();
322                 return !final_success;
323         }
324
325         // Let the frontend parse and remove all arguments that it knows
326         pimpl_->application_.reset(createApplication(argc, argv));
327
328         // Reestablish our defaults, as Qt overwrites them
329         // after createApplication()
330         setLocale();//???
331
332         // Parse and remove all known arguments in the LyX singleton
333         // Give an error for all remaining ones.
334         int exit_status = init(argc, argv);
335         if (exit_status) {
336                 // Kill the application object before exiting.
337                 pimpl_->application_.reset();
338                 use_gui = false;
339                 prepareExit();
340                 return exit_status;
341         }
342
343         // If not otherwise specified by a command line option or
344         // by preferences, we default to reuse a running instance.
345         if (run_mode == PREFERRED)
346                 run_mode = USE_REMOTE;
347
348         // FIXME
349         /* Create a CoreApplication class that will provide the main event loop
350         * and the socket callback registering. With Qt, only QtCore
351         * library would be needed.
352         * When this is done, a server_mode could be created and the following two
353         * line would be moved out from here.
354         * However, note that the first of the two lines below triggers the
355         * "single instance" behavior, which should occur right at this point.
356         */
357         // Note: socket callback must be registered after init(argc, argv)
358         // such that package().temp_dir() is properly initialized.
359         pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
360         pimpl_->lyx_socket_.reset(new ServerSocket(
361                         FileName(package().temp_dir().absFileName() + "/lyxsocket")));
362
363         // Start the real execution loop.
364         if (!theServer().deferredLoadingToOtherInstance())
365                 exit_status = pimpl_->application_->exec();
366         else if (!pimpl_->files_to_load_.empty()) {
367                 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
368                 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
369                 lyxerr << _("The following files could not be loaded:") << endl;
370                 for (; it != end; ++it)
371                         lyxerr << *it << endl;
372         }
373
374         prepareExit();
375
376         return exit_status;
377 }
378
379
380 void LyX::prepareExit()
381 {
382         // Clear the clipboard and selection stack:
383         cap::clearCutStack();
384         cap::clearSelection();
385
386         // Write the index file of the converter cache
387         ConverterCache::get().writeIndex();
388
389         // close buffers first
390         pimpl_->buffer_list_.closeAll();
391
392         // register session changes and shutdown server and socket
393         if (use_gui) {
394                 if (pimpl_->session_)
395                         pimpl_->session_->writeFile();
396                 pimpl_->session_.reset();
397                 pimpl_->lyx_server_.reset();
398                 pimpl_->lyx_socket_.reset();
399         }
400
401         // do any other cleanup procedures now
402         if (package().temp_dir() != package().system_temp_dir()) {
403                 string const abs_tmpdir = package().temp_dir().absFileName();
404                 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
405                         docstring const msg =
406                                 bformat(_("%1$s does not appear like a LyX created temporary directory."),
407                                 from_utf8(abs_tmpdir));
408                         Alert::warning(_("Cannot remove temporary directory"), msg);
409                 } else {
410                         LYXERR(Debug::INFO, "Deleting tmp dir "
411                                 << package().temp_dir().absFileName());
412                         if (!package().temp_dir().destroyDirectory()) {
413                                 docstring const msg =
414                                         bformat(_("Unable to remove the temporary directory %1$s"),
415                                         from_utf8(package().temp_dir().absFileName()));
416                                 Alert::warning(_("Unable to remove temporary directory"), msg);
417                         }
418                 }
419         }
420
421         // Kill the application object before exiting. This avoids crashes
422         // when exiting on Linux.
423         pimpl_->application_.reset();
424 }
425
426
427 void LyX::earlyExit(int status)
428 {
429         LATTEST(pimpl_->application_.get());
430         // LyX::pimpl_::application_ is not initialised at this
431         // point so it's safe to just exit after some cleanup.
432         prepareExit();
433         exit(status);
434 }
435
436
437 int LyX::init(int & argc, char * argv[])
438 {
439         // check for any spurious extra arguments
440         // other than documents
441         for (int argi = 1; argi < argc ; ++argi) {
442                 if (argv[argi][0] == '-') {
443                         lyxerr << to_utf8(
444                                 bformat(_("Wrong command line option `%1$s'. Exiting."),
445                                 from_utf8(os::utf8_argv(argi)))) << endl;
446                         return EXIT_FAILURE;
447                 }
448         }
449
450         // Initialization of LyX (reads lyxrc and more)
451         LYXERR(Debug::INIT, "Initializing LyX::init...");
452         bool success = init();
453         LYXERR(Debug::INIT, "Initializing LyX::init...done");
454         if (!success)
455                 return EXIT_FAILURE;
456
457         // Remaining arguments are assumed to be files to load.
458         for (int argi = 1; argi < argc; ++argi)
459                 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
460
461         if (!use_gui && pimpl_->files_to_load_.empty()) {
462                 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
463                 return EXIT_FAILURE;
464         }
465
466         if (first_start) {
467                 pimpl_->files_to_load_.push_back(
468                         i18nLibFileSearch("examples", "splash.lyx").absFileName());
469         }
470
471         return EXIT_SUCCESS;
472 }
473
474
475 bool LyX::loadFiles()
476 {
477         LATTEST(!use_gui);
478         bool success = true;
479         vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
480         vector<string>::const_iterator end = pimpl_->files_to_load_.end();
481
482         for (; it != end; ++it) {
483                 // get absolute path of file and add ".lyx" to
484                 // the filename if necessary
485                 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
486                         may_not_exist);
487
488                 if (fname.empty())
489                         continue;
490
491                 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
492                 if (buf->loadLyXFile() == Buffer::ReadSuccess) {
493                         ErrorList const & el = buf->errorList("Parse");
494                         if (!el.empty())
495                                 for_each(el.begin(), el.end(),
496                                 bind(&LyX::printError, this, _1));
497                 }
498                 else {
499                         pimpl_->buffer_list_.release(buf);
500                         docstring const error_message =
501                                 bformat(_("LyX failed to load the following file: %1$s"),
502                                 from_utf8(fname.absFileName()));
503                         lyxerr << to_utf8(error_message) << endl;
504                         success = false;
505                 }
506         }
507         return success;
508 }
509
510
511 void execBatchCommands()
512 {
513         LAPPERR(singleton_);
514         singleton_->execCommands();
515 }
516
517
518 void LyX::execCommands()
519 {
520         // The advantage of doing this here is that the event loop
521         // is already started. So any need for interaction will be
522         // aknowledged.
523
524         // if reconfiguration is needed.
525         if (LayoutFileList::get().empty()) {
526                 switch (Alert::prompt(
527                         _("No textclass is found"),
528                         _("LyX will only have minimal functionality because no textclasses "
529                                 "have been found. You can either try to reconfigure LyX normally, "
530                                 "try to reconfigure without checking your LaTeX installation, or continue."),
531                         0, 2,
532                         _("&Reconfigure"),
533                         _("&Without LaTeX"),
534                         _("&Continue")))
535                 {
536                 case 0:
537                         // regular reconfigure
538                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
539                         break;
540                 case 1:
541                         // reconfigure --without-latex-config
542                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
543                                 " --without-latex-config"));
544                         break;
545                 default:
546                         break;
547                 }
548         }
549
550         // create the first main window
551         lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
552
553         if (!pimpl_->files_to_load_.empty()) {
554                 // if some files were specified at command-line we assume that the
555                 // user wants to edit *these* files and not to restore the session.
556                 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
557                         lyx::dispatch(
558                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
559                 }
560                 // clear this list to save a few bytes of RAM
561                 pimpl_->files_to_load_.clear();
562         } else
563                 pimpl_->application_->restoreGuiSession();
564
565         // Execute batch commands if available
566         if (pimpl_->batch_commands.empty())
567                 return;
568
569         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
570         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
571         for (; bcit != bcend; ++bcit) {
572                 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
573                 lyx::dispatch(lyxaction.lookupFunc(*bcit));
574         }
575 }
576
577
578 /*
579 Signals and Windows
580 ===================
581 The SIGHUP signal does not exist on Windows and does not need to be handled.
582
583 Windows handles SIGFPE and SIGSEGV signals as expected.
584
585 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
586 cause a new thread to be spawned. This may well result in unexpected
587 behaviour by the single-threaded LyX.
588
589 SIGTERM signals will come only from another process actually sending
590 that signal using 'raise' in Windows' POSIX compatability layer. It will
591 not come from the general "terminate process" methods that everyone
592 actually uses (and which can't be trapped). Killing an app 'politely' on
593 Windows involves first sending a WM_CLOSE message, something that is
594 caught already by the Qt frontend.
595
596 For more information see:
597
598 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
599 ...signals are mostly useless on Windows for a variety of reasons that are
600 Windows specific...
601
602 'UNIX Application Migration Guide, Chapter 9'
603 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
604
605 'How To Terminate an Application "Cleanly" in Win32'
606 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
607 */
608 extern "C" {
609
610 static void error_handler(int err_sig)
611 {
612         // Throw away any signals other than the first one received.
613         static sig_atomic_t handling_error = false;
614         if (handling_error)
615                 return;
616         handling_error = true;
617
618         // We have received a signal indicating a fatal error, so
619         // try and save the data ASAP.
620         emergencyCleanup();
621
622         // These lyxerr calls may or may not work:
623
624         // Signals are asynchronous, so the main program may be in a very
625         // fragile state when a signal is processed and thus while a signal
626         // handler function executes.
627         // In general, therefore, we should avoid performing any
628         // I/O operations or calling most library and system functions from
629         // signal handlers.
630
631         // This shouldn't matter here, however, as we've already invoked
632         // emergencyCleanup.
633         docstring msg;
634         switch (err_sig) {
635 #ifdef SIGHUP
636         case SIGHUP:
637                 msg = _("SIGHUP signal caught!\nBye.");
638                 break;
639 #endif
640         case SIGFPE:
641                 msg = _("SIGFPE signal caught!\nBye.");
642                 break;
643         case SIGSEGV:
644                 msg = _("SIGSEGV signal caught!\n"
645                           "Sorry, you have found a bug in LyX, "
646                           "hope you have not lost any data.\n"
647                           "Please read the bug-reporting instructions "
648                           "in 'Help->Introduction' and send us a bug report, "
649                           "if necessary. Thanks!\nBye.");
650                 break;
651         case SIGINT:
652         case SIGTERM:
653                 // no comments
654                 break;
655         }
656
657         if (!msg.empty()) {
658                 lyxerr << "\nlyx: " << msg << endl;
659                 // try to make a GUI message
660                 Alert::error(_("LyX crashed!"), msg, true);
661         }
662
663         // Deinstall the signal handlers
664 #ifdef SIGHUP
665         signal(SIGHUP, SIG_DFL);
666 #endif
667         signal(SIGINT, SIG_DFL);
668         signal(SIGFPE, SIG_DFL);
669         signal(SIGSEGV, SIG_DFL);
670         signal(SIGTERM, SIG_DFL);
671
672 #ifdef SIGHUP
673         if (err_sig == SIGSEGV ||
674                 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
675 #else
676         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
677 #endif
678 #ifdef _MSC_VER
679                 // with abort() it crashes again.
680                 exit(err_sig);
681 #else
682                 abort();
683 #endif
684         }
685
686         exit(0);
687 }
688
689 }
690
691
692 void LyX::printError(ErrorItem const & ei)
693 {
694         docstring tmp = _("LyX: ") + ei.error + char_type(':')
695                 + ei.description;
696         cerr << to_utf8(tmp) << endl;
697 }
698
699 #if defined (USE_MACOSX_PACKAGING)
700 namespace {
701         // Unexposed--extract an environment variable name from its NAME=VALUE
702         // representation
703         std::string varname(const char* line)
704         {
705                 size_t nameLen = strcspn(line, "=");
706                 if (nameLen == strlen(line)) {
707                         return std::string();
708                 } else {
709                         return std::string(line, nameLen);
710                 }
711         }
712 }
713
714 void cleanDuplicateEnvVars()
715 {
716         std::set<std::string> seen;
717         std::set<std::string> dupes;
718
719         // Create a list of the environment variables that appear more than once
720         for (char **read = *_NSGetEnviron(); *read; read++) {
721                 std::string name = varname(*read);
722                 if (name.size() == 0) {
723                         continue;
724                 }
725                 if (seen.find(name) != seen.end()) {
726                         dupes.insert(name);
727                 } else {
728                         seen.insert(name);
729                 }
730         }
731
732         // Loop over the list of duplicated variables
733         for (std::set<std::string>::iterator dupe = dupes.begin(); dupe != dupes.end(); dupe++) {
734                 const char *name = (*dupe).c_str();
735                 char *val = getenv(name);
736                 if (val != NULL) {
737                         LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
738                         // unsetenv removes *all* instances of the variable from the environment
739                         unsetenv(name);
740
741                         // replace with the value from getenv (in practice appears to be the
742                         // first value in the list)
743                         setenv(name, val, 0);
744                 }
745         }
746 }
747 #endif
748
749 bool LyX::init()
750 {
751 #ifdef SIGHUP
752         signal(SIGHUP, error_handler);
753 #endif
754         signal(SIGFPE, error_handler);
755         signal(SIGSEGV, error_handler);
756         signal(SIGINT, error_handler);
757         signal(SIGTERM, error_handler);
758         // SIGPIPE can be safely ignored.
759
760 #if defined (USE_MACOSX_PACKAGING)
761         cleanDuplicateEnvVars();
762 #endif
763
764         lyxrc.tempdir_path = package().temp_dir().absFileName();
765         lyxrc.document_path = package().document_dir().absFileName();
766
767         if (lyxrc.example_path.empty()) {
768                 lyxrc.example_path = addPath(package().system_support().absFileName(),
769                                               "examples");
770         }
771         if (lyxrc.template_path.empty()) {
772                 lyxrc.template_path = addPath(package().system_support().absFileName(),
773                                               "templates");
774         }
775
776         // init LyXDir environment variable
777         string const lyx_dir = package().lyx_dir().absFileName();
778         LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
779         if (!setEnv("LyXDir", lyx_dir))
780                 LYXERR(Debug::INIT, "\t... failed!");
781
782         if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
783                 // -userdir was given on the command line.
784                 // Make it available to child processes, otherwise tex2lyx
785                 // would not find all layout files, and other converters might
786                 // use it as well.
787                 string const user_dir = package().user_support().absFileName();
788                 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
789                                     << user_dir << '"');
790                 if (!setEnv(LYX_USERDIR_VER, user_dir))
791                         LYXERR(Debug::INIT, "\t... failed!");
792         }
793
794         //
795         // Read configuration files
796         //
797
798         // This one may have been distributed along with LyX.
799         if (!readRcFile("lyxrc.dist"))
800                 return false;
801
802         // Set the PATH correctly.
803 #if !defined (USE_POSIX_PACKAGING)
804         // Add the directory containing the LyX executable to the path
805         // so that LyX can find things like tex2lyx.
806         if (package().build_support().empty())
807                 prependEnvPath("PATH", package().binary_dir().absFileName());
808 #endif
809         if (!lyxrc.path_prefix.empty())
810                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
811
812         // Check that user LyX directory is ok.
813         {
814                 string const lock_file = package().getConfigureLockName();
815                 int fd = fileLock(lock_file.c_str());
816
817                 if (queryUserLyXDir(package().explicit_user_support())) {
818                         package().reconfigureUserLyXDir("");
819                 }
820                 fileUnlock(fd, lock_file.c_str());
821         }
822
823         if (!use_gui) {
824                 // No need for a splash when there is no GUI
825                 first_start = false;
826                 // Default is to overwrite the main file during export, unless
827                 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
828                 if (force_overwrite == UNSPECIFIED) {
829                         string const what = getEnv("LYX_FORCE_OVERWRITE");
830                         if (what == "all")
831                                 force_overwrite = ALL_FILES;
832                         else if (what == "none")
833                                 force_overwrite = NO_FILES;
834                         else
835                                 force_overwrite = MAIN_FILE;
836                 }
837         }
838
839         // This one is generated in user_support directory by lib/configure.py.
840         if (!readRcFile("lyxrc.defaults"))
841                 return false;
842
843         // Query the OS to know what formats are viewed natively
844         formats.setAutoOpen();
845
846         // Read lyxrc.dist again to be able to override viewer auto-detection.
847         readRcFile("lyxrc.dist");
848
849         system_lyxrc = lyxrc;
850         system_formats = formats;
851         pimpl_->system_converters_ = pimpl_->converters_;
852         pimpl_->system_movers_ = pimpl_->movers_;
853         system_lcolor = lcolor;
854
855         // This one is edited through the preferences dialog.
856         if (!readRcFile("preferences", true))
857                 return false;
858
859         // The language may have been set to someting useful through prefs
860         setLocale();
861
862         if (!readEncodingsFile("encodings", "unicodesymbols"))
863                 return false;
864         if (!readLanguagesFile("languages"))
865                 return false;
866
867         LYXERR(Debug::INIT, "Reading layouts...");
868         // Load the layouts
869         LayoutFileList::get().read();
870         //...and the modules
871         theModuleList.read();
872
873         // read keymap and ui files in batch mode as well
874         // because InsetInfo needs to know these to produce
875         // the correct output
876
877         // Set up command definitions
878         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
879
880         // FIXME
881         // Set up bindings
882         pimpl_->toplevel_keymap_.read("site");
883         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
884         // load user bind file user.bind
885         pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
886
887         if (lyxerr.debugging(Debug::LYXRC))
888                 lyxrc.print();
889
890         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
891         // Prepend path prefix a second time to take the user preferences into a account
892         if (!lyxrc.path_prefix.empty())
893                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
894
895         FileName const document_path(lyxrc.document_path);
896         if (document_path.exists() && document_path.isDirectory())
897                 package().document_dir() = document_path;
898
899         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
900         if (package().temp_dir().empty()) {
901                 Alert::error(_("Could not create temporary directory"),
902                              bformat(_("Could not create a temporary directory in\n"
903                                                        "\"%1$s\"\n"
904                                                            "Make sure that this path exists and is writable and try again."),
905                                      from_utf8(lyxrc.tempdir_path)));
906                 // createLyXTmpDir() tries sufficiently hard to create a
907                 // usable temp dir, so the probability to come here is
908                 // close to zero. We therefore don't try to overcome this
909                 // problem with e.g. asking the user for a new path and
910                 // trying again but simply exit.
911                 return false;
912         }
913
914         LYXERR(Debug::INIT, "LyX tmp dir: `"
915                             << package().temp_dir().absFileName() << '\'');
916
917         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
918         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
919
920         // This must happen after package initialization and after lyxrc is
921         // read, therefore it can't be done by a static object.
922         ConverterCache::init();
923
924         return true;
925 }
926
927
928 void emergencyCleanup()
929 {
930         // what to do about tmpfiles is non-obvious. we would
931         // like to delete any we find, but our lyxdir might
932         // contain documents etc. which might be helpful on
933         // a crash
934
935         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
936         if (use_gui) {
937                 if (singleton_->pimpl_->lyx_server_)
938                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
939                 singleton_->pimpl_->lyx_server_.reset();
940                 singleton_->pimpl_->lyx_socket_.reset();
941         }
942 }
943
944
945 bool LyX::queryUserLyXDir(bool explicit_userdir)
946 {
947         // Does user directory exist?
948         FileName const sup = package().user_support();
949         if (sup.exists() && sup.isDirectory()) {
950                 first_start = false;
951
952                 return configFileNeedsUpdate("lyxrc.defaults")
953                         || configFileNeedsUpdate("lyxmodules.lst")
954                         || configFileNeedsUpdate("textclass.lst")
955                         || configFileNeedsUpdate("packages.lst");
956         }
957
958         first_start = !explicit_userdir;
959
960         // If the user specified explicitly a directory, ask whether
961         // to create it. If the user says "no", then exit.
962         if (explicit_userdir &&
963             Alert::prompt(
964                     _("Missing user LyX directory"),
965                     bformat(_("You have specified a non-existent user "
966                                            "LyX directory, %1$s.\n"
967                                            "It is needed to keep your own configuration."),
968                             from_utf8(package().user_support().absFileName())),
969                     1, 0,
970                     _("&Create directory"),
971                     _("&Exit LyX"))) {
972                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
973                 earlyExit(EXIT_FAILURE);
974         }
975
976         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
977                           from_utf8(sup.absFileName()))) << endl;
978
979         if (!sup.createDirectory(0755)) {
980                 // Failed, so let's exit.
981                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
982                        << endl;
983                 earlyExit(EXIT_FAILURE);
984         }
985
986         return true;
987 }
988
989
990 bool LyX::readRcFile(string const & name, bool check_format)
991 {
992         LYXERR(Debug::INIT, "About to read " << name << "... ");
993
994         FileName const lyxrc_path = libFileSearch(string(), name);
995         if (lyxrc_path.empty()) {
996                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
997                 // FIXME
998                 // This was the previous logic, but can it be right??
999                 return true;
1000         }
1001         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1002         bool const success = lyxrc.read(lyxrc_path, check_format);
1003         if (!success)
1004                 showFileError(name);
1005         return success;
1006 }
1007
1008 // Read the languages file `name'
1009 bool LyX::readLanguagesFile(string const & name)
1010 {
1011         LYXERR(Debug::INIT, "About to read " << name << "...");
1012
1013         FileName const lang_path = libFileSearch(string(), name);
1014         if (lang_path.empty()) {
1015                 showFileError(name);
1016                 return false;
1017         }
1018         languages.read(lang_path);
1019         return true;
1020 }
1021
1022
1023 // Read the encodings file `name'
1024 bool LyX::readEncodingsFile(string const & enc_name,
1025                             string const & symbols_name)
1026 {
1027         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1028                             << symbols_name << "...");
1029
1030         FileName const symbols_path = libFileSearch(string(), symbols_name);
1031         if (symbols_path.empty()) {
1032                 showFileError(symbols_name);
1033                 return false;
1034         }
1035
1036         FileName const enc_path = libFileSearch(string(), enc_name);
1037         if (enc_path.empty()) {
1038                 showFileError(enc_name);
1039                 return false;
1040         }
1041         encodings.read(enc_path, symbols_path);
1042         return true;
1043 }
1044
1045
1046 namespace {
1047
1048 /// return the the number of arguments consumed
1049 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
1050
1051 int parse_dbg(string const & arg, string const &, string &)
1052 {
1053         if (arg.empty()) {
1054                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1055                 Debug::showTags(cout);
1056                 exit(0);
1057         }
1058         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1059
1060         lyxerr.setLevel(Debug::value(arg));
1061         Debug::showLevel(lyxerr, lyxerr.level());
1062         return 1;
1063 }
1064
1065
1066 int parse_help(string const &, string const &, string &)
1067 {
1068         cout <<
1069                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1070                   "Command line switches (case sensitive):\n"
1071                   "\t-help              summarize LyX usage\n"
1072                   "\t-userdir dir       set user directory to dir\n"
1073                   "\t-sysdir dir        set system directory to dir\n"
1074                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1075                   "\t-dbg feature[,feature]...\n"
1076                   "                  select the features to debug.\n"
1077                   "                  Type `lyx -dbg' to see the list of features\n"
1078                   "\t-x [--execute] command\n"
1079                   "                  where command is a lyx command.\n"
1080                   "\t-e [--export] fmt\n"
1081                   "                  where fmt is the export format of choice. Look in\n"
1082                   "                  Tools->Preferences->File Handling->File Formats->Short Name\n"
1083                   "                  to see which parameter (which differs from the format name\n"
1084                   "                  in the File->Export menu) should be passed.\n"
1085                   "                  Note that the order of -e and -x switches matters.\n"
1086                   "\t-E [--export-to] fmt filename\n"
1087                   "                  where fmt is the export format of choice (see --export),\n"
1088                   "                  and filename is the destination filename.\n"
1089                   "\t-i [--import] fmt file.xxx\n"
1090                   "                  where fmt is the import format of choice\n"
1091                   "                  and file.xxx is the file to be imported.\n"
1092                   "\t-f [--force-overwrite] what\n"
1093                   "                  where what is either `all', `main' or `none',\n"
1094                   "                  specifying whether all files, main file only, or no files,\n"
1095                   "                  respectively, are to be overwritten during a batch export.\n"
1096                   "                  Anything else is equivalent to `all', but is not consumed.\n"
1097                   "\t-n [--no-remote]\n"
1098                   "                  open documents in a new instance\n"
1099                   "\t-r [--remote]\n"
1100                   "                  open documents in an already running instance\n"
1101                   "                  (a working lyxpipe is needed)\n"
1102                   "\t-batch    execute commands without launching GUI and exit.\n"
1103                   "\t-version  summarize version and build info\n"
1104                                "Check the LyX man page for more details.")) << endl;
1105         exit(0);
1106         return 0;
1107 }
1108
1109
1110 int parse_version(string const &, string const &, string &)
1111 {
1112         cout << "LyX " << lyx_version
1113                << " (" << lyx_release_date << ")" << endl;
1114         if (string(lyx_git_commit_hash) != "none")
1115                 cout << to_utf8(_("  Git commit hash "))
1116                      << string(lyx_git_commit_hash).substr(0,8) << endl;
1117         cout << to_utf8(bformat(_("Built on %1$s[[date]], %2$s[[time]]"),
1118                 from_ascii(lyx_build_date), from_ascii(lyx_build_time))) << endl;
1119         cout << lyx_version_info << endl;
1120         exit(0);
1121         return 0;
1122 }
1123
1124
1125 int parse_sysdir(string const & arg, string const &, string &)
1126 {
1127         if (arg.empty()) {
1128                 Alert::error(_("No system directory"),
1129                         _("Missing directory for -sysdir switch"));
1130                 exit(1);
1131         }
1132         cl_system_support = arg;
1133         return 1;
1134 }
1135
1136
1137 int parse_userdir(string const & arg, string const &, string &)
1138 {
1139         if (arg.empty()) {
1140                 Alert::error(_("No user directory"),
1141                         _("Missing directory for -userdir switch"));
1142                 exit(1);
1143         }
1144         cl_user_support = arg;
1145         return 1;
1146 }
1147
1148
1149 int parse_execute(string const & arg, string const &, string & batch)
1150 {
1151         if (arg.empty()) {
1152                 Alert::error(_("Incomplete command"),
1153                         _("Missing command string after --execute switch"));
1154                 exit(1);
1155         }
1156         batch = arg;
1157         return 1;
1158 }
1159
1160
1161 int parse_export_to(string const & type, string const & output_file, string & batch)
1162 {
1163         if (type.empty()) {
1164                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1165                                          "--export-to switch")) << endl;
1166                 exit(1);
1167         }
1168         if (output_file.empty()) {
1169                 lyxerr << to_utf8(_("Missing destination filename after "
1170                                          "--export-to switch")) << endl;
1171                 exit(1);
1172         }
1173         batch = "buffer-export " + type + " " + output_file;
1174         use_gui = false;
1175         return 2;
1176 }
1177
1178
1179 int parse_export(string const & type, string const &, string & batch)
1180 {
1181         if (type.empty()) {
1182                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1183                                          "--export switch")) << endl;
1184                 exit(1);
1185         }
1186         batch = "buffer-export " + type;
1187         use_gui = false;
1188         return 1;
1189 }
1190
1191
1192 int parse_import(string const & type, string const & file, string & batch)
1193 {
1194         if (type.empty()) {
1195                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1196                                          "--import switch")) << endl;
1197                 exit(1);
1198         }
1199         if (file.empty()) {
1200                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1201                 exit(1);
1202         }
1203         batch = "buffer-import " + type + ' ' + file;
1204         return 2;
1205 }
1206
1207
1208 int parse_geometry(string const & arg1, string const &, string &)
1209 {
1210         geometryArg = arg1;
1211         // don't remove "-geometry", it will be pruned out later in the
1212         // frontend if need be.
1213         return -1;
1214 }
1215
1216
1217 int parse_batch(string const &, string const &, string &)
1218 {
1219         use_gui = false;
1220         return 0;
1221 }
1222
1223
1224 int parse_noremote(string const &, string const &, string &)
1225 {
1226         run_mode = NEW_INSTANCE;
1227         return 0;
1228 }
1229
1230
1231 int parse_remote(string const &, string const &, string &)
1232 {
1233         run_mode = USE_REMOTE;
1234         return 0;
1235 }
1236
1237
1238 int parse_force(string const & arg, string const &, string &)
1239 {
1240         if (arg == "all") {
1241                 force_overwrite = ALL_FILES;
1242                 return 1;
1243         } else if (arg == "main") {
1244                 force_overwrite = MAIN_FILE;
1245                 return 1;
1246         } else if (arg == "none") {
1247                 force_overwrite = NO_FILES;
1248                 return 1;
1249         }
1250         force_overwrite = ALL_FILES;
1251         return 0;
1252 }
1253
1254
1255 } // namespace anon
1256
1257
1258 void LyX::easyParse(int & argc, char * argv[])
1259 {
1260         map<string, cmd_helper> cmdmap;
1261
1262         cmdmap["-dbg"] = parse_dbg;
1263         cmdmap["-help"] = parse_help;
1264         cmdmap["--help"] = parse_help;
1265         cmdmap["-version"] = parse_version;
1266         cmdmap["--version"] = parse_version;
1267         cmdmap["-sysdir"] = parse_sysdir;
1268         cmdmap["-userdir"] = parse_userdir;
1269         cmdmap["-x"] = parse_execute;
1270         cmdmap["--execute"] = parse_execute;
1271         cmdmap["-e"] = parse_export;
1272         cmdmap["--export"] = parse_export;
1273         cmdmap["-E"] = parse_export_to;
1274         cmdmap["--export-to"] = parse_export_to;
1275         cmdmap["-i"] = parse_import;
1276         cmdmap["--import"] = parse_import;
1277         cmdmap["-geometry"] = parse_geometry;
1278         cmdmap["-batch"] = parse_batch;
1279         cmdmap["-f"] = parse_force;
1280         cmdmap["--force-overwrite"] = parse_force;
1281         cmdmap["-n"] = parse_noremote;
1282         cmdmap["--no-remote"] = parse_noremote;
1283         cmdmap["-r"] = parse_remote;
1284         cmdmap["--remote"] = parse_remote;
1285
1286         for (int i = 1; i < argc; ++i) {
1287                 map<string, cmd_helper>::const_iterator it
1288                         = cmdmap.find(argv[i]);
1289
1290                 // don't complain if not found - may be parsed later
1291                 if (it == cmdmap.end())
1292                         continue;
1293
1294                 string const arg =
1295                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1296                 string const arg2 =
1297                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1298
1299                 string batch;
1300                 int const remove = 1 + it->second(arg, arg2, batch);
1301                 if (!batch.empty())
1302                         pimpl_->batch_commands.push_back(batch);
1303
1304                 // Now, remove used arguments by shifting
1305                 // the following ones remove places down.
1306                 if (remove > 0) {
1307                         os::remove_internal_args(i, remove);
1308                         argc -= remove;
1309                         for (int j = i; j < argc; ++j)
1310                                 argv[j] = argv[j + remove];
1311                         --i;
1312                 }
1313         }
1314 }
1315
1316
1317 FuncStatus getStatus(FuncRequest const & action)
1318 {
1319         LAPPERR(theApp());
1320         return theApp()->getStatus(action);
1321 }
1322
1323
1324 void dispatch(FuncRequest const & action)
1325 {
1326         LAPPERR(theApp());
1327         return theApp()->dispatch(action);
1328 }
1329
1330
1331 void dispatch(FuncRequest const & action, DispatchResult & dr)
1332 {
1333         LAPPERR(theApp());
1334         return theApp()->dispatch(action, dr);
1335 }
1336
1337
1338 vector<string> & theFilesToLoad()
1339 {
1340         LAPPERR(singleton_);
1341         return singleton_->pimpl_->files_to_load_;
1342 }
1343
1344
1345 BufferList & theBufferList()
1346 {
1347         LAPPERR(singleton_);
1348         return singleton_->pimpl_->buffer_list_;
1349 }
1350
1351
1352 Server & theServer()
1353 {
1354         // FIXME: this should not be use_gui dependent
1355         LWARNIF(use_gui);
1356         LAPPERR(singleton_);
1357         return *singleton_->pimpl_->lyx_server_.get();
1358 }
1359
1360
1361 ServerSocket & theServerSocket()
1362 {
1363         // FIXME: this should not be use_gui dependent
1364         LWARNIF(use_gui);
1365         LAPPERR(singleton_);
1366         return *singleton_->pimpl_->lyx_socket_.get();
1367 }
1368
1369
1370 KeyMap & theTopLevelKeymap()
1371 {
1372         LAPPERR(singleton_);
1373         return singleton_->pimpl_->toplevel_keymap_;
1374 }
1375
1376
1377 Converters & theConverters()
1378 {
1379         LAPPERR(singleton_);
1380         return  singleton_->pimpl_->converters_;
1381 }
1382
1383
1384 Converters & theSystemConverters()
1385 {
1386         LAPPERR(singleton_);
1387         return  singleton_->pimpl_->system_converters_;
1388 }
1389
1390
1391 Movers & theMovers()
1392 {
1393         LAPPERR(singleton_);
1394         return singleton_->pimpl_->movers_;
1395 }
1396
1397
1398 Mover const & getMover(string  const & fmt)
1399 {
1400         LAPPERR(singleton_);
1401         return singleton_->pimpl_->movers_(fmt);
1402 }
1403
1404
1405 void setMover(string const & fmt, string const & command)
1406 {
1407         LAPPERR(singleton_);
1408         singleton_->pimpl_->movers_.set(fmt, command);
1409 }
1410
1411
1412 Movers & theSystemMovers()
1413 {
1414         LAPPERR(singleton_);
1415         return singleton_->pimpl_->system_movers_;
1416 }
1417
1418
1419 Messages const & getMessages(string const & language)
1420 {
1421         LAPPERR(singleton_);
1422         return singleton_->messages(language);
1423 }
1424
1425
1426 Messages const & getGuiMessages()
1427 {
1428         LAPPERR(singleton_);
1429         return singleton_->messages(Messages::guiLanguage());
1430 }
1431
1432
1433 Session & theSession()
1434 {
1435         LAPPERR(singleton_);
1436         return *singleton_->pimpl_->session_.get();
1437 }
1438
1439
1440 LaTeXFonts & theLaTeXFonts()
1441 {
1442         LAPPERR(singleton_);
1443         if (!singleton_->pimpl_->latexfonts_)
1444                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1445         return *singleton_->pimpl_->latexfonts_;
1446 }
1447
1448
1449 CmdDef & theTopLevelCmdDef()
1450 {
1451         LAPPERR(singleton_);
1452         return singleton_->pimpl_->toplevel_cmddef_;
1453 }
1454
1455
1456 SpellChecker * theSpellChecker()
1457 {
1458         if (!singleton_->pimpl_->spell_checker_)
1459                 setSpellChecker();
1460         return singleton_->pimpl_->spell_checker_;
1461 }
1462
1463
1464 void setSpellChecker()
1465 {
1466         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1467                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1468
1469         if (lyxrc.spellchecker == "native") {
1470 #if defined(USE_MACOSX_PACKAGING)
1471                 if (!singleton_->pimpl_->apple_spell_checker_)
1472                         singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1473                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1474 #else
1475                 singleton_->pimpl_->spell_checker_ = 0;
1476 #endif
1477         } else if (lyxrc.spellchecker == "aspell") {
1478 #if defined(USE_ASPELL)
1479                 if (!singleton_->pimpl_->aspell_checker_)
1480                         singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1481                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1482 #else
1483                 singleton_->pimpl_->spell_checker_ = 0;
1484 #endif
1485         } else if (lyxrc.spellchecker == "enchant") {
1486 #if defined(USE_ENCHANT)
1487                 if (!singleton_->pimpl_->enchant_checker_)
1488                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1489                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1490 #else
1491                 singleton_->pimpl_->spell_checker_ = 0;
1492 #endif
1493         } else if (lyxrc.spellchecker == "hunspell") {
1494 #if defined(USE_HUNSPELL)
1495                 if (!singleton_->pimpl_->hunspell_checker_)
1496                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1497                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1498 #else
1499                 singleton_->pimpl_->spell_checker_ = 0;
1500 #endif
1501         } else {
1502                 singleton_->pimpl_->spell_checker_ = 0;
1503         }
1504         if (singleton_->pimpl_->spell_checker_) {
1505                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1506                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1507         }
1508 }
1509
1510 } // namespace lyx