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