]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
41c69f7dae4d77f031290dfed4b0dede36b8de3d
[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                         lyx::dispatch(
567                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
568                 }
569                 // clear this list to save a few bytes of RAM
570                 pimpl_->files_to_load_.clear();
571         } else
572                 pimpl_->application_->restoreGuiSession();
573
574         // Execute batch commands if available
575         if (pimpl_->batch_commands.empty())
576                 return;
577
578         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
579         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
580         for (; bcit != bcend; ++bcit) {
581                 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
582                 lyx::dispatch(lyxaction.lookupFunc(*bcit));
583         }
584 }
585
586
587 /*
588 Signals and Windows
589 ===================
590 The SIGHUP signal does not exist on Windows and does not need to be handled.
591
592 Windows handles SIGFPE and SIGSEGV signals as expected.
593
594 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
595 cause a new thread to be spawned. This may well result in unexpected
596 behaviour by the single-threaded LyX.
597
598 SIGTERM signals will come only from another process actually sending
599 that signal using 'raise' in Windows' POSIX compatability layer. It will
600 not come from the general "terminate process" methods that everyone
601 actually uses (and which can't be trapped). Killing an app 'politely' on
602 Windows involves first sending a WM_CLOSE message, something that is
603 caught already by the Qt frontend.
604
605 For more information see:
606
607 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
608 ...signals are mostly useless on Windows for a variety of reasons that are
609 Windows specific...
610
611 'UNIX Application Migration Guide, Chapter 9'
612 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
613
614 'How To Terminate an Application "Cleanly" in Win32'
615 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
616 */
617 extern "C" {
618
619 static void error_handler(int err_sig)
620 {
621         // Throw away any signals other than the first one received.
622         static sig_atomic_t handling_error = false;
623         if (handling_error)
624                 return;
625         handling_error = true;
626
627         // We have received a signal indicating a fatal error, so
628         // try and save the data ASAP.
629         emergencyCleanup();
630
631         // These lyxerr calls may or may not work:
632
633         // Signals are asynchronous, so the main program may be in a very
634         // fragile state when a signal is processed and thus while a signal
635         // handler function executes.
636         // In general, therefore, we should avoid performing any
637         // I/O operations or calling most library and system functions from
638         // signal handlers.
639
640         // This shouldn't matter here, however, as we've already invoked
641         // emergencyCleanup.
642         docstring msg;
643         switch (err_sig) {
644 #ifdef SIGHUP
645         case SIGHUP:
646                 msg = _("SIGHUP signal caught!\nBye.");
647                 break;
648 #endif
649         case SIGFPE:
650                 msg = _("SIGFPE signal caught!\nBye.");
651                 break;
652         case SIGSEGV:
653                 msg = _("SIGSEGV signal caught!\n"
654                           "Sorry, you have found a bug in LyX, "
655                           "hope you have not lost any data.\n"
656                           "Please read the bug-reporting instructions "
657                           "in 'Help->Introduction' and send us a bug report, "
658                           "if necessary. Thanks!\nBye.");
659                 break;
660         case SIGINT:
661         case SIGTERM:
662                 // no comments
663                 break;
664         }
665
666         if (!msg.empty()) {
667                 lyxerr << "\nlyx: " << msg << endl;
668                 // try to make a GUI message
669                 Alert::error(_("LyX crashed!"), msg);
670         }
671
672         // Deinstall the signal handlers
673 #ifdef SIGHUP
674         signal(SIGHUP, SIG_DFL);
675 #endif
676         signal(SIGINT, SIG_DFL);
677         signal(SIGFPE, SIG_DFL);
678         signal(SIGSEGV, SIG_DFL);
679         signal(SIGTERM, SIG_DFL);
680
681 #ifdef SIGHUP
682         if (err_sig == SIGSEGV ||
683                 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
684 #else
685         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
686 #endif
687 #ifdef _MSC_VER
688                 // with abort() it crashes again.
689                 exit(err_sig);
690 #else
691                 abort();
692 #endif
693         }
694
695         exit(0);
696 }
697
698 }
699
700
701 void LyX::printError(ErrorItem const & ei)
702 {
703         docstring tmp = _("LyX: ") + ei.error + char_type(':')
704                 + ei.description;
705         cerr << to_utf8(tmp) << endl;
706 }
707
708
709 bool LyX::init()
710 {
711 #ifdef SIGHUP
712         signal(SIGHUP, error_handler);
713 #endif
714         signal(SIGFPE, error_handler);
715         signal(SIGSEGV, error_handler);
716         signal(SIGINT, error_handler);
717         signal(SIGTERM, error_handler);
718         // SIGPIPE can be safely ignored.
719
720         lyxrc.tempdir_path = package().temp_dir().absFileName();
721         lyxrc.document_path = package().document_dir().absFileName();
722
723         if (lyxrc.example_path.empty()) {
724                 lyxrc.example_path = addPath(package().system_support().absFileName(),
725                                               "examples");
726         }
727         if (lyxrc.template_path.empty()) {
728                 lyxrc.template_path = addPath(package().system_support().absFileName(),
729                                               "templates");
730         }
731
732         // init LyXDir environment variable
733         string const lyx_dir = package().lyx_dir().absFileName();
734         LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
735         if (!setEnv("LyXDir", lyx_dir))
736                 LYXERR(Debug::INIT, "\t... failed!");
737
738         if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
739                 // -userdir was given on the command line.
740                 // Make it available to child processes, otherwise tex2lyx
741                 // would not find all layout files, and other converters might
742                 // use it as well.
743                 string const user_dir = package().user_support().absFileName();
744                 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
745                                     << user_dir << '"');
746                 if (!setEnv(LYX_USERDIR_VER, user_dir))
747                         LYXERR(Debug::INIT, "\t... failed!");
748         }
749
750         //
751         // Read configuration files
752         //
753
754         // This one may have been distributed along with LyX.
755         if (!readRcFile("lyxrc.dist"))
756                 return false;
757
758         // Set the PATH correctly.
759 #if !defined (USE_POSIX_PACKAGING)
760         // Add the directory containing the LyX executable to the path
761         // so that LyX can find things like tex2lyx.
762         if (package().build_support().empty())
763                 prependEnvPath("PATH", package().binary_dir().absFileName());
764 #endif
765         if (!lyxrc.path_prefix.empty())
766                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
767
768         // Check that user LyX directory is ok.
769         {
770                 string const lock_file = package().user_support().absFileName() + ".lyx_configure_lock";
771                 int fd = fileLock(lock_file.c_str());
772
773                 if (queryUserLyXDir(package().explicit_user_support())) {
774                         reconfigureUserLyXDir();
775                 }
776                 fileUnlock(fd, lock_file.c_str());
777         }
778
779         if (!use_gui) {
780                 // No need for a splash when there is no GUI
781                 first_start = false;
782                 // Default is to overwrite the main file during export, unless
783                 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
784                 if (force_overwrite == UNSPECIFIED) {
785                         string const what = getEnv("LYX_FORCE_OVERWRITE");
786                         if (what == "all")
787                                 force_overwrite = ALL_FILES;
788                         else if (what == "none")
789                                 force_overwrite = NO_FILES;
790                         else
791                                 force_overwrite = MAIN_FILE;
792                 }
793         }
794
795         // This one is generated in user_support directory by lib/configure.py.
796         if (!readRcFile("lyxrc.defaults"))
797                 return false;
798
799         // Query the OS to know what formats are viewed natively
800         formats.setAutoOpen();
801
802         // Read lyxrc.dist again to be able to override viewer auto-detection.
803         readRcFile("lyxrc.dist");
804
805         system_lyxrc = lyxrc;
806         system_formats = formats;
807         pimpl_->system_converters_ = pimpl_->converters_;
808         pimpl_->system_movers_ = pimpl_->movers_;
809         system_lcolor = lcolor;
810
811         // This one is edited through the preferences dialog.
812         if (!readRcFile("preferences", true))
813                 return false;
814
815         // The language may have been set to someting useful through prefs
816         setLocale();
817
818         if (!readEncodingsFile("encodings", "unicodesymbols"))
819                 return false;
820         if (!readLanguagesFile("languages"))
821                 return false;
822
823         LYXERR(Debug::INIT, "Reading layouts...");
824         // Load the layouts
825         LayoutFileList::get().read();
826         //...and the modules
827         theModuleList.read();
828
829         // read keymap and ui files in batch mode as well
830         // because InsetInfo needs to know these to produce
831         // the correct output
832
833         // Set up command definitions
834         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
835
836         // FIXME
837         // Set up bindings
838         pimpl_->toplevel_keymap_.read("site");
839         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
840         // load user bind file user.bind
841         pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
842
843         if (lyxerr.debugging(Debug::LYXRC))
844                 lyxrc.print();
845
846         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
847         // Prepend path prefix a second time to take the user preferences into a account
848         if (!lyxrc.path_prefix.empty())
849                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
850
851         FileName const document_path(lyxrc.document_path);
852         if (document_path.exists() && document_path.isDirectory())
853                 package().document_dir() = document_path;
854
855         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
856         if (package().temp_dir().empty()) {
857                 Alert::error(_("Could not create temporary directory"),
858                              bformat(_("Could not create a temporary directory in\n"
859                                                        "\"%1$s\"\n"
860                                                            "Make sure that this path exists and is writable and try again."),
861                                      from_utf8(lyxrc.tempdir_path)));
862                 // createLyXTmpDir() tries sufficiently hard to create a
863                 // usable temp dir, so the probability to come here is
864                 // close to zero. We therefore don't try to overcome this
865                 // problem with e.g. asking the user for a new path and
866                 // trying again but simply exit.
867                 return false;
868         }
869
870         LYXERR(Debug::INIT, "LyX tmp dir: `"
871                             << package().temp_dir().absFileName() << '\'');
872
873         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
874         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
875
876         // This must happen after package initialization and after lyxrc is
877         // read, therefore it can't be done by a static object.
878         ConverterCache::init();
879
880         return true;
881 }
882
883
884 void emergencyCleanup()
885 {
886         // what to do about tmpfiles is non-obvious. we would
887         // like to delete any we find, but our lyxdir might
888         // contain documents etc. which might be helpful on
889         // a crash
890
891         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
892         if (use_gui) {
893                 if (singleton_->pimpl_->lyx_server_)
894                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
895                 singleton_->pimpl_->lyx_server_.reset();
896                 singleton_->pimpl_->lyx_socket_.reset();
897         }
898 }
899
900
901 // return true if file does not exist or is older than configure.py.
902 static bool needsUpdate(string const & file)
903 {
904         // We cannot initialize configure_script directly because the package
905         // is not initialized yet when static objects are constructed.
906         static FileName configure_script;
907         static bool firstrun = true;
908         if (firstrun) {
909                 configure_script =
910                         FileName(addName(package().system_support().absFileName(),
911                                 "configure.py"));
912                 firstrun = false;
913         }
914
915         FileName absfile =
916                 FileName(addName(package().user_support().absFileName(), file));
917         return !absfile.exists()
918                 || configure_script.lastModified() > absfile.lastModified();
919 }
920
921
922 bool LyX::queryUserLyXDir(bool explicit_userdir)
923 {
924         // Does user directory exist?
925         FileName const sup = package().user_support();
926         if (sup.exists() && sup.isDirectory()) {
927                 first_start = false;
928
929                 return needsUpdate("lyxrc.defaults")
930                         || needsUpdate("lyxmodules.lst")
931                         || needsUpdate("textclass.lst")
932                         || needsUpdate("packages.lst");
933         }
934
935         first_start = !explicit_userdir;
936
937         // If the user specified explicitly a directory, ask whether
938         // to create it. If the user says "no", then exit.
939         if (explicit_userdir &&
940             Alert::prompt(
941                     _("Missing user LyX directory"),
942                     bformat(_("You have specified a non-existent user "
943                                            "LyX directory, %1$s.\n"
944                                            "It is needed to keep your own configuration."),
945                             from_utf8(package().user_support().absFileName())),
946                     1, 0,
947                     _("&Create directory"),
948                     _("&Exit LyX"))) {
949                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
950                 earlyExit(EXIT_FAILURE);
951         }
952
953         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
954                           from_utf8(sup.absFileName()))) << endl;
955
956         if (!sup.createDirectory(0755)) {
957                 // Failed, so let's exit.
958                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
959                        << endl;
960                 earlyExit(EXIT_FAILURE);
961         }
962
963         return true;
964 }
965
966
967 bool LyX::readRcFile(string const & name, bool check_format)
968 {
969         LYXERR(Debug::INIT, "About to read " << name << "... ");
970
971         FileName const lyxrc_path = libFileSearch(string(), name);
972         if (lyxrc_path.empty()) {
973                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
974                 // FIXME
975                 // This was the previous logic, but can it be right??
976                 return true;
977         }
978         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
979         bool const success = lyxrc.read(lyxrc_path, check_format);
980         if (!success)
981                 showFileError(name);
982         return success;
983 }
984
985 // Read the languages file `name'
986 bool LyX::readLanguagesFile(string const & name)
987 {
988         LYXERR(Debug::INIT, "About to read " << name << "...");
989
990         FileName const lang_path = libFileSearch(string(), name);
991         if (lang_path.empty()) {
992                 showFileError(name);
993                 return false;
994         }
995         languages.read(lang_path);
996         return true;
997 }
998
999
1000 // Read the encodings file `name'
1001 bool LyX::readEncodingsFile(string const & enc_name,
1002                             string const & symbols_name)
1003 {
1004         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1005                             << symbols_name << "...");
1006
1007         FileName const symbols_path = libFileSearch(string(), symbols_name);
1008         if (symbols_path.empty()) {
1009                 showFileError(symbols_name);
1010                 return false;
1011         }
1012
1013         FileName const enc_path = libFileSearch(string(), enc_name);
1014         if (enc_path.empty()) {
1015                 showFileError(enc_name);
1016                 return false;
1017         }
1018         encodings.read(enc_path, symbols_path);
1019         return true;
1020 }
1021
1022
1023 namespace {
1024
1025 /// return the the number of arguments consumed
1026 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
1027
1028 int parse_dbg(string const & arg, string const &, string &)
1029 {
1030         if (arg.empty()) {
1031                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1032                 Debug::showTags(cout);
1033                 exit(0);
1034         }
1035         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1036
1037         lyxerr.setLevel(Debug::value(arg));
1038         Debug::showLevel(lyxerr, lyxerr.level());
1039         return 1;
1040 }
1041
1042
1043 int parse_help(string const &, string const &, string &)
1044 {
1045         cout <<
1046                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1047                   "Command line switches (case sensitive):\n"
1048                   "\t-help              summarize LyX usage\n"
1049                   "\t-userdir dir       set user directory to dir\n"
1050                   "\t-sysdir dir        set system directory to dir\n"
1051                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1052                   "\t-dbg feature[,feature]...\n"
1053                   "                  select the features to debug.\n"
1054                   "                  Type `lyx -dbg' to see the list of features\n"
1055                   "\t-x [--execute] command\n"
1056                   "                  where command is a lyx command.\n"
1057                   "\t-e [--export] fmt\n"
1058                   "                  where fmt is the export format of choice. Look in\n"
1059                   "                  Tools->Preferences->File Handling->File Formats->Short Name\n"
1060                   "                  to see which parameter (which differs from the format name\n"
1061                   "                  in the File->Export menu) should be passed.\n"
1062                   "                  Note that the order of -e and -x switches matters.\n"
1063                   "\t-E [--export-to] fmt filename\n"
1064                   "                  where fmt is the export format of choice (see --export),\n"
1065                   "                  and filename is the destination filename.\n"
1066                   "\t-i [--import] fmt file.xxx\n"
1067                   "                  where fmt is the import format of choice\n"
1068                   "                  and file.xxx is the file to be imported.\n"
1069                   "\t-f [--force-overwrite] what\n"
1070                   "                  where what is either `all', `main' or `none',\n"
1071                   "                  specifying whether all files, main file only, or no files,\n"
1072                   "                  respectively, are to be overwritten during a batch export.\n"
1073                   "                  Anything else is equivalent to `all', but is not consumed.\n"
1074                   "\t-n [--no-remote]\n"
1075                   "                  open documents in a new instance\n"
1076                   "\t-r [--remote]\n"
1077                   "                  open documents in an already running instance\n"
1078                   "                  (a working lyxpipe is needed)\n"
1079                   "\t-batch    execute commands without launching GUI and exit.\n"
1080                   "\t-version  summarize version and build info\n"
1081                                "Check the LyX man page for more details.")) << endl;
1082         exit(0);
1083         return 0;
1084 }
1085
1086
1087 int parse_version(string const &, string const &, string &)
1088 {
1089         cout << "LyX " << lyx_version
1090                << " (" << lyx_release_date << ")" << endl;
1091         cout << to_utf8(bformat(_("Built on %1$s[[date]], %2$s[[time]]"),
1092                 from_ascii(__DATE__), from_ascii(__TIME__))) << endl;
1093
1094         cout << lyx_version_info << endl;
1095         exit(0);
1096         return 0;
1097 }
1098
1099
1100 int parse_sysdir(string const & arg, string const &, string &)
1101 {
1102         if (arg.empty()) {
1103                 Alert::error(_("No system directory"),
1104                         _("Missing directory for -sysdir switch"));
1105                 exit(1);
1106         }
1107         cl_system_support = arg;
1108         return 1;
1109 }
1110
1111
1112 int parse_userdir(string const & arg, string const &, string &)
1113 {
1114         if (arg.empty()) {
1115                 Alert::error(_("No user directory"),
1116                         _("Missing directory for -userdir switch"));
1117                 exit(1);
1118         }
1119         cl_user_support = arg;
1120         return 1;
1121 }
1122
1123
1124 int parse_execute(string const & arg, string const &, string & batch)
1125 {
1126         if (arg.empty()) {
1127                 Alert::error(_("Incomplete command"),
1128                         _("Missing command string after --execute switch"));
1129                 exit(1);
1130         }
1131         batch = arg;
1132         return 1;
1133 }
1134
1135
1136 int parse_export_to(string const & type, string const & output_file, string & batch)
1137 {
1138         if (type.empty()) {
1139                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1140                                          "--export-to switch")) << endl;
1141                 exit(1);
1142         }
1143         if (output_file.empty()) {
1144                 lyxerr << to_utf8(_("Missing destination filename after "
1145                                          "--export-to switch")) << endl;
1146                 exit(1);
1147         }
1148         batch = "buffer-export " + type + " " + output_file;
1149         use_gui = false;
1150         return 2;
1151 }
1152
1153
1154 int parse_export(string const & type, string const &, string & batch)
1155 {
1156         if (type.empty()) {
1157                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1158                                          "--export switch")) << endl;
1159                 exit(1);
1160         }
1161         batch = "buffer-export " + type;
1162         use_gui = false;
1163         return 1;
1164 }
1165
1166
1167 int parse_import(string const & type, string const & file, string & batch)
1168 {
1169         if (type.empty()) {
1170                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1171                                          "--import switch")) << endl;
1172                 exit(1);
1173         }
1174         if (file.empty()) {
1175                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1176                 exit(1);
1177         }
1178         batch = "buffer-import " + type + ' ' + file;
1179         return 2;
1180 }
1181
1182
1183 int parse_geometry(string const & arg1, string const &, string &)
1184 {
1185         geometryArg = arg1;
1186         // don't remove "-geometry", it will be pruned out later in the
1187         // frontend if need be.
1188         return -1;
1189 }
1190
1191
1192 int parse_batch(string const &, string const &, string &)
1193 {
1194         use_gui = false;
1195         return 0;
1196 }
1197
1198
1199 int parse_noremote(string const &, string const &, string &)
1200 {
1201         run_mode = NEW_INSTANCE;
1202         return 0;
1203 }
1204
1205
1206 int parse_remote(string const &, string const &, string &)
1207 {
1208         run_mode = USE_REMOTE;
1209         return 0;
1210 }
1211
1212
1213 int parse_force(string const & arg, string const &, string &)
1214 {
1215         if (arg == "all") {
1216                 force_overwrite = ALL_FILES;
1217                 return 1;
1218         } else if (arg == "main") {
1219                 force_overwrite = MAIN_FILE;
1220                 return 1;
1221         } else if (arg == "none") {
1222                 force_overwrite = NO_FILES;
1223                 return 1;
1224         }
1225         force_overwrite = ALL_FILES;
1226         return 0;
1227 }
1228
1229
1230 } // namespace anon
1231
1232
1233 void LyX::easyParse(int & argc, char * argv[])
1234 {
1235         map<string, cmd_helper> cmdmap;
1236
1237         cmdmap["-dbg"] = parse_dbg;
1238         cmdmap["-help"] = parse_help;
1239         cmdmap["--help"] = parse_help;
1240         cmdmap["-version"] = parse_version;
1241         cmdmap["--version"] = parse_version;
1242         cmdmap["-sysdir"] = parse_sysdir;
1243         cmdmap["-userdir"] = parse_userdir;
1244         cmdmap["-x"] = parse_execute;
1245         cmdmap["--execute"] = parse_execute;
1246         cmdmap["-e"] = parse_export;
1247         cmdmap["--export"] = parse_export;
1248         cmdmap["-E"] = parse_export_to;
1249         cmdmap["--export-to"] = parse_export_to;
1250         cmdmap["-i"] = parse_import;
1251         cmdmap["--import"] = parse_import;
1252         cmdmap["-geometry"] = parse_geometry;
1253         cmdmap["-batch"] = parse_batch;
1254         cmdmap["-f"] = parse_force;
1255         cmdmap["--force-overwrite"] = parse_force;
1256         cmdmap["-n"] = parse_noremote;
1257         cmdmap["--no-remote"] = parse_noremote;
1258         cmdmap["-r"] = parse_remote;
1259         cmdmap["--remote"] = parse_remote;
1260
1261         for (int i = 1; i < argc; ++i) {
1262                 map<string, cmd_helper>::const_iterator it
1263                         = cmdmap.find(argv[i]);
1264
1265                 // don't complain if not found - may be parsed later
1266                 if (it == cmdmap.end())
1267                         continue;
1268
1269                 string const arg =
1270                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1271                 string const arg2 =
1272                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1273
1274                 string batch;
1275                 int const remove = 1 + it->second(arg, arg2, batch);
1276                 if (!batch.empty())
1277                         pimpl_->batch_commands.push_back(batch);
1278
1279                 // Now, remove used arguments by shifting
1280                 // the following ones remove places down.
1281                 if (remove > 0) {
1282                         os::remove_internal_args(i, remove);
1283                         argc -= remove;
1284                         for (int j = i; j < argc; ++j)
1285                                 argv[j] = argv[j + remove];
1286                         --i;
1287                 }
1288         }
1289 }
1290
1291
1292 FuncStatus getStatus(FuncRequest const & action)
1293 {
1294         LAPPERR(theApp());
1295         return theApp()->getStatus(action);
1296 }
1297
1298
1299 void dispatch(FuncRequest const & action)
1300 {
1301         LAPPERR(theApp());
1302         return theApp()->dispatch(action);
1303 }
1304
1305
1306 void dispatch(FuncRequest const & action, DispatchResult & dr)
1307 {
1308         LAPPERR(theApp());
1309         return theApp()->dispatch(action, dr);
1310 }
1311
1312
1313 vector<string> & theFilesToLoad()
1314 {
1315         LAPPERR(singleton_);
1316         return singleton_->pimpl_->files_to_load_;
1317 }
1318
1319
1320 BufferList & theBufferList()
1321 {
1322         LAPPERR(singleton_);
1323         return singleton_->pimpl_->buffer_list_;
1324 }
1325
1326
1327 Server & theServer()
1328 {
1329         // FIXME: this should not be use_gui dependent
1330         LWARNIF(use_gui);
1331         LAPPERR(singleton_);
1332         return *singleton_->pimpl_->lyx_server_.get();
1333 }
1334
1335
1336 ServerSocket & theServerSocket()
1337 {
1338         // FIXME: this should not be use_gui dependent
1339         LWARNIF(use_gui);
1340         LAPPERR(singleton_);
1341         return *singleton_->pimpl_->lyx_socket_.get();
1342 }
1343
1344
1345 KeyMap & theTopLevelKeymap()
1346 {
1347         LAPPERR(singleton_);
1348         return singleton_->pimpl_->toplevel_keymap_;
1349 }
1350
1351
1352 Converters & theConverters()
1353 {
1354         LAPPERR(singleton_);
1355         return  singleton_->pimpl_->converters_;
1356 }
1357
1358
1359 Converters & theSystemConverters()
1360 {
1361         LAPPERR(singleton_);
1362         return  singleton_->pimpl_->system_converters_;
1363 }
1364
1365
1366 Movers & theMovers()
1367 {
1368         LAPPERR(singleton_);
1369         return singleton_->pimpl_->movers_;
1370 }
1371
1372
1373 Mover const & getMover(string  const & fmt)
1374 {
1375         LAPPERR(singleton_);
1376         return singleton_->pimpl_->movers_(fmt);
1377 }
1378
1379
1380 void setMover(string const & fmt, string const & command)
1381 {
1382         LAPPERR(singleton_);
1383         singleton_->pimpl_->movers_.set(fmt, command);
1384 }
1385
1386
1387 Movers & theSystemMovers()
1388 {
1389         LAPPERR(singleton_);
1390         return singleton_->pimpl_->system_movers_;
1391 }
1392
1393
1394 Messages const & getMessages(string const & language)
1395 {
1396         LAPPERR(singleton_);
1397         return singleton_->messages(language);
1398 }
1399
1400
1401 Messages const & getGuiMessages()
1402 {
1403         LAPPERR(singleton_);
1404         return singleton_->messages(Messages::guiLanguage());
1405 }
1406
1407
1408 Session & theSession()
1409 {
1410         LAPPERR(singleton_);
1411         return *singleton_->pimpl_->session_.get();
1412 }
1413
1414
1415 LaTeXFonts & theLaTeXFonts()
1416 {
1417         LAPPERR(singleton_);
1418         if (!singleton_->pimpl_->latexfonts_)
1419                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1420         return *singleton_->pimpl_->latexfonts_;
1421 }
1422
1423
1424 CmdDef & theTopLevelCmdDef()
1425 {
1426         LAPPERR(singleton_);
1427         return singleton_->pimpl_->toplevel_cmddef_;
1428 }
1429
1430
1431 SpellChecker * theSpellChecker()
1432 {
1433         if (!singleton_->pimpl_->spell_checker_)
1434                 setSpellChecker();
1435         return singleton_->pimpl_->spell_checker_;
1436 }
1437
1438
1439 void setSpellChecker()
1440 {
1441         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1442                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1443
1444         if (lyxrc.spellchecker == "native") {
1445 #if defined(USE_MACOSX_PACKAGING)
1446                 if (!singleton_->pimpl_->apple_spell_checker_)
1447                         singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1448                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1449 #else
1450                 singleton_->pimpl_->spell_checker_ = 0;
1451 #endif
1452         } else if (lyxrc.spellchecker == "aspell") {
1453 #if defined(USE_ASPELL)
1454                 if (!singleton_->pimpl_->aspell_checker_)
1455                         singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1456                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1457 #else
1458                 singleton_->pimpl_->spell_checker_ = 0;
1459 #endif
1460         } else if (lyxrc.spellchecker == "enchant") {
1461 #if defined(USE_ENCHANT)
1462                 if (!singleton_->pimpl_->enchant_checker_)
1463                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1464                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1465 #else
1466                 singleton_->pimpl_->spell_checker_ = 0;
1467 #endif
1468         } else if (lyxrc.spellchecker == "hunspell") {
1469 #if defined(USE_HUNSPELL)
1470                 if (!singleton_->pimpl_->hunspell_checker_)
1471                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1472                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1473 #else
1474                 singleton_->pimpl_->spell_checker_ = 0;
1475 #endif
1476         } else {
1477                 singleton_->pimpl_->spell_checker_ = 0;
1478         }
1479         if (singleton_->pimpl_->spell_checker_) {
1480                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1481                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1482         }
1483 }
1484
1485 } // namespace lyx