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