]> git.lyx.org Git - features.git/blob - src/LyX.cpp
Make -userdir available to child processes
[features.git] / src / LyX.cpp
1 /**
2  * \file LyX.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16 #include <version.h>
17
18 #include "LyX.h"
19
20 #include "AppleSpellChecker.h"
21 #include "AspellChecker.h"
22 #include "Buffer.h"
23 #include "BufferList.h"
24 #include "CmdDef.h"
25 #include "ColorSet.h"
26 #include "ConverterCache.h"
27 #include "Converter.h"
28 #include "CutAndPaste.h"
29 #include "EnchantChecker.h"
30 #include "Encoding.h"
31 #include "ErrorList.h"
32 #include "Format.h"
33 #include "FuncStatus.h"
34 #include "HunspellChecker.h"
35 #include "KeyMap.h"
36 #include "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         if (!lyxrc.path_prefix.empty())
848                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
849
850         FileName const document_path(lyxrc.document_path);
851         if (document_path.exists() && document_path.isDirectory())
852                 package().document_dir() = document_path;
853
854         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
855         if (package().temp_dir().empty()) {
856                 Alert::error(_("Could not create temporary directory"),
857                              bformat(_("Could not create a temporary directory in\n"
858                                                        "\"%1$s\"\n"
859                                                            "Make sure that this path exists and is writable and try again."),
860                                      from_utf8(lyxrc.tempdir_path)));
861                 // createLyXTmpDir() tries sufficiently hard to create a
862                 // usable temp dir, so the probability to come here is
863                 // close to zero. We therefore don't try to overcome this
864                 // problem with e.g. asking the user for a new path and
865                 // trying again but simply exit.
866                 return false;
867         }
868
869         LYXERR(Debug::INIT, "LyX tmp dir: `"
870                             << package().temp_dir().absFileName() << '\'');
871
872         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
873         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
874
875         // This must happen after package initialization and after lyxrc is
876         // read, therefore it can't be done by a static object.
877         ConverterCache::init();
878
879         return true;
880 }
881
882
883 void emergencyCleanup()
884 {
885         // what to do about tmpfiles is non-obvious. we would
886         // like to delete any we find, but our lyxdir might
887         // contain documents etc. which might be helpful on
888         // a crash
889
890         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
891         if (use_gui) {
892                 if (singleton_->pimpl_->lyx_server_)
893                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
894                 singleton_->pimpl_->lyx_server_.reset();
895                 singleton_->pimpl_->lyx_socket_.reset();
896         }
897 }
898
899
900 // return true if file does not exist or is older than configure.py.
901 static bool needsUpdate(string const & file)
902 {
903         // We cannot initialize configure_script directly because the package
904         // is not initialized yet when static objects are constructed.
905         static FileName configure_script;
906         static bool firstrun = true;
907         if (firstrun) {
908                 configure_script =
909                         FileName(addName(package().system_support().absFileName(),
910                                 "configure.py"));
911                 firstrun = false;
912         }
913
914         FileName absfile =
915                 FileName(addName(package().user_support().absFileName(), file));
916         return !absfile.exists()
917                 || configure_script.lastModified() > absfile.lastModified();
918 }
919
920
921 bool LyX::queryUserLyXDir(bool explicit_userdir)
922 {
923         // Does user directory exist?
924         FileName const sup = package().user_support();
925         if (sup.exists() && sup.isDirectory()) {
926                 first_start = false;
927
928                 return needsUpdate("lyxrc.defaults")
929                         || needsUpdate("lyxmodules.lst")
930                         || needsUpdate("textclass.lst")
931                         || needsUpdate("packages.lst");
932         }
933
934         first_start = !explicit_userdir;
935
936         // If the user specified explicitly a directory, ask whether
937         // to create it. If the user says "no", then exit.
938         if (explicit_userdir &&
939             Alert::prompt(
940                     _("Missing user LyX directory"),
941                     bformat(_("You have specified a non-existent user "
942                                            "LyX directory, %1$s.\n"
943                                            "It is needed to keep your own configuration."),
944                             from_utf8(package().user_support().absFileName())),
945                     1, 0,
946                     _("&Create directory"),
947                     _("&Exit LyX"))) {
948                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
949                 earlyExit(EXIT_FAILURE);
950         }
951
952         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
953                           from_utf8(sup.absFileName()))) << endl;
954
955         if (!sup.createDirectory(0755)) {
956                 // Failed, so let's exit.
957                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
958                        << endl;
959                 earlyExit(EXIT_FAILURE);
960         }
961
962         return true;
963 }
964
965
966 bool LyX::readRcFile(string const & name, bool check_format)
967 {
968         LYXERR(Debug::INIT, "About to read " << name << "... ");
969
970         FileName const lyxrc_path = libFileSearch(string(), name);
971         if (lyxrc_path.empty()) {
972                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
973                 // FIXME
974                 // This was the previous logic, but can it be right??
975                 return true;
976         }
977         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
978         bool const success = lyxrc.read(lyxrc_path, check_format);
979         if (!success)
980                 showFileError(name);
981         return success;
982 }
983
984 // Read the languages file `name'
985 bool LyX::readLanguagesFile(string const & name)
986 {
987         LYXERR(Debug::INIT, "About to read " << name << "...");
988
989         FileName const lang_path = libFileSearch(string(), name);
990         if (lang_path.empty()) {
991                 showFileError(name);
992                 return false;
993         }
994         languages.read(lang_path);
995         return true;
996 }
997
998
999 // Read the encodings file `name'
1000 bool LyX::readEncodingsFile(string const & enc_name,
1001                             string const & symbols_name)
1002 {
1003         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1004                             << symbols_name << "...");
1005
1006         FileName const symbols_path = libFileSearch(string(), symbols_name);
1007         if (symbols_path.empty()) {
1008                 showFileError(symbols_name);
1009                 return false;
1010         }
1011
1012         FileName const enc_path = libFileSearch(string(), enc_name);
1013         if (enc_path.empty()) {
1014                 showFileError(enc_name);
1015                 return false;
1016         }
1017         encodings.read(enc_path, symbols_path);
1018         return true;
1019 }
1020
1021
1022 namespace {
1023
1024 /// return the the number of arguments consumed
1025 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
1026
1027 int parse_dbg(string const & arg, string const &, string &)
1028 {
1029         if (arg.empty()) {
1030                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1031                 Debug::showTags(cout);
1032                 exit(0);
1033         }
1034         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1035
1036         lyxerr.setLevel(Debug::value(arg));
1037         Debug::showLevel(lyxerr, lyxerr.level());
1038         return 1;
1039 }
1040
1041
1042 int parse_help(string const &, string const &, string &)
1043 {
1044         cout <<
1045                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1046                   "Command line switches (case sensitive):\n"
1047                   "\t-help              summarize LyX usage\n"
1048                   "\t-userdir dir       set user directory to dir\n"
1049                   "\t-sysdir dir        set system directory to dir\n"
1050                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1051                   "\t-dbg feature[,feature]...\n"
1052                   "                  select the features to debug.\n"
1053                   "                  Type `lyx -dbg' to see the list of features\n"
1054                   "\t-x [--execute] command\n"
1055                   "                  where command is a lyx command.\n"
1056                   "\t-e [--export] fmt\n"
1057                   "                  where fmt is the export format of choice. Look in\n"
1058                   "                  Tools->Preferences->File Handling->File Formats->Short Name\n"
1059                   "                  to see which parameter (which differs from the format name\n"
1060                   "                  in the File->Export menu) should be passed.\n"
1061                   "                  Note that the order of -e and -x switches matters.\n"
1062                   "\t-E [--export-to] fmt filename\n"
1063                   "                  where fmt is the export format of choice (see --export),\n"
1064                   "                  and filename is the destination filename.\n"
1065                   "\t-i [--import] fmt file.xxx\n"
1066                   "                  where fmt is the import format of choice\n"
1067                   "                  and file.xxx is the file to be imported.\n"
1068                   "\t-f [--force-overwrite] what\n"
1069                   "                  where what is either `all', `main' or `none',\n"
1070                   "                  specifying whether all files, main file only, or no files,\n"
1071                   "                  respectively, are to be overwritten during a batch export.\n"
1072                   "                  Anything else is equivalent to `all', but is not consumed.\n"
1073                   "\t-n [--no-remote]\n"
1074                   "                  open documents in a new instance\n"
1075                   "\t-r [--remote]\n"
1076                   "                  open documents in an already running instance\n"
1077                   "                  (a working lyxpipe is needed)\n"
1078                   "\t-batch    execute commands without launching GUI and exit.\n"
1079                   "\t-version  summarize version and build info\n"
1080                                "Check the LyX man page for more details.")) << endl;
1081         exit(0);
1082         return 0;
1083 }
1084
1085
1086 int parse_version(string const &, string const &, string &)
1087 {
1088         cout << "LyX " << lyx_version
1089                << " (" << lyx_release_date << ")" << endl;
1090         cout << to_utf8(bformat(_("Built on %1$s[[date]], %2$s[[time]]"),
1091                 from_ascii(__DATE__), from_ascii(__TIME__))) << endl;
1092
1093         cout << lyx_version_info << endl;
1094         exit(0);
1095         return 0;
1096 }
1097
1098
1099 int parse_sysdir(string const & arg, string const &, string &)
1100 {
1101         if (arg.empty()) {
1102                 Alert::error(_("No system directory"),
1103                         _("Missing directory for -sysdir switch"));
1104                 exit(1);
1105         }
1106         cl_system_support = arg;
1107         return 1;
1108 }
1109
1110
1111 int parse_userdir(string const & arg, string const &, string &)
1112 {
1113         if (arg.empty()) {
1114                 Alert::error(_("No user directory"),
1115                         _("Missing directory for -userdir switch"));
1116                 exit(1);
1117         }
1118         cl_user_support = arg;
1119         return 1;
1120 }
1121
1122
1123 int parse_execute(string const & arg, string const &, string & batch)
1124 {
1125         if (arg.empty()) {
1126                 Alert::error(_("Incomplete command"),
1127                         _("Missing command string after --execute switch"));
1128                 exit(1);
1129         }
1130         batch = arg;
1131         return 1;
1132 }
1133
1134
1135 int parse_export_to(string const & type, string const & output_file, string & batch)
1136 {
1137         if (type.empty()) {
1138                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1139                                          "--export-to switch")) << endl;
1140                 exit(1);
1141         }
1142         if (output_file.empty()) {
1143                 lyxerr << to_utf8(_("Missing destination filename after "
1144                                          "--export-to switch")) << endl;
1145                 exit(1);
1146         }
1147         batch = "buffer-export " + type + " " + output_file;
1148         use_gui = false;
1149         return 2;
1150 }
1151
1152
1153 int parse_export(string const & type, string const &, string & batch)
1154 {
1155         if (type.empty()) {
1156                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1157                                          "--export switch")) << endl;
1158                 exit(1);
1159         }
1160         batch = "buffer-export " + type;
1161         use_gui = false;
1162         return 1;
1163 }
1164
1165
1166 int parse_import(string const & type, string const & file, string & batch)
1167 {
1168         if (type.empty()) {
1169                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1170                                          "--import switch")) << endl;
1171                 exit(1);
1172         }
1173         if (file.empty()) {
1174                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1175                 exit(1);
1176         }
1177         batch = "buffer-import " + type + ' ' + file;
1178         return 2;
1179 }
1180
1181
1182 int parse_geometry(string const & arg1, string const &, string &)
1183 {
1184         geometryArg = arg1;
1185         // don't remove "-geometry", it will be pruned out later in the
1186         // frontend if need be.
1187         return -1;
1188 }
1189
1190
1191 int parse_batch(string const &, string const &, string &)
1192 {
1193         use_gui = false;
1194         return 0;
1195 }
1196
1197
1198 int parse_noremote(string const &, string const &, string &)
1199 {
1200         run_mode = NEW_INSTANCE;
1201         return 0;
1202 }
1203
1204
1205 int parse_remote(string const &, string const &, string &)
1206 {
1207         run_mode = USE_REMOTE;
1208         return 0;
1209 }
1210
1211
1212 int parse_force(string const & arg, string const &, string &)
1213 {
1214         if (arg == "all") {
1215                 force_overwrite = ALL_FILES;
1216                 return 1;
1217         } else if (arg == "main") {
1218                 force_overwrite = MAIN_FILE;
1219                 return 1;
1220         } else if (arg == "none") {
1221                 force_overwrite = NO_FILES;
1222                 return 1;
1223         }
1224         force_overwrite = ALL_FILES;
1225         return 0;
1226 }
1227
1228
1229 } // namespace anon
1230
1231
1232 void LyX::easyParse(int & argc, char * argv[])
1233 {
1234         map<string, cmd_helper> cmdmap;
1235
1236         cmdmap["-dbg"] = parse_dbg;
1237         cmdmap["-help"] = parse_help;
1238         cmdmap["--help"] = parse_help;
1239         cmdmap["-version"] = parse_version;
1240         cmdmap["--version"] = parse_version;
1241         cmdmap["-sysdir"] = parse_sysdir;
1242         cmdmap["-userdir"] = parse_userdir;
1243         cmdmap["-x"] = parse_execute;
1244         cmdmap["--execute"] = parse_execute;
1245         cmdmap["-e"] = parse_export;
1246         cmdmap["--export"] = parse_export;
1247         cmdmap["-E"] = parse_export_to;
1248         cmdmap["--export-to"] = parse_export_to;
1249         cmdmap["-i"] = parse_import;
1250         cmdmap["--import"] = parse_import;
1251         cmdmap["-geometry"] = parse_geometry;
1252         cmdmap["-batch"] = parse_batch;
1253         cmdmap["-f"] = parse_force;
1254         cmdmap["--force-overwrite"] = parse_force;
1255         cmdmap["-n"] = parse_noremote;
1256         cmdmap["--no-remote"] = parse_noremote;
1257         cmdmap["-r"] = parse_remote;
1258         cmdmap["--remote"] = parse_remote;
1259
1260         for (int i = 1; i < argc; ++i) {
1261                 map<string, cmd_helper>::const_iterator it
1262                         = cmdmap.find(argv[i]);
1263
1264                 // don't complain if not found - may be parsed later
1265                 if (it == cmdmap.end())
1266                         continue;
1267
1268                 string const arg =
1269                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1270                 string const arg2 =
1271                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1272
1273                 string batch;
1274                 int const remove = 1 + it->second(arg, arg2, batch);
1275                 if (!batch.empty())
1276                         pimpl_->batch_commands.push_back(batch);
1277
1278                 // Now, remove used arguments by shifting
1279                 // the following ones remove places down.
1280                 if (remove > 0) {
1281                         os::remove_internal_args(i, remove);
1282                         argc -= remove;
1283                         for (int j = i; j < argc; ++j)
1284                                 argv[j] = argv[j + remove];
1285                         --i;
1286                 }
1287         }
1288 }
1289
1290
1291 FuncStatus getStatus(FuncRequest const & action)
1292 {
1293         LAPPERR(theApp());
1294         return theApp()->getStatus(action);
1295 }
1296
1297
1298 void dispatch(FuncRequest const & action)
1299 {
1300         LAPPERR(theApp());
1301         return theApp()->dispatch(action);
1302 }
1303
1304
1305 void dispatch(FuncRequest const & action, DispatchResult & dr)
1306 {
1307         LAPPERR(theApp());
1308         return theApp()->dispatch(action, dr);
1309 }
1310
1311
1312 vector<string> & theFilesToLoad()
1313 {
1314         LAPPERR(singleton_);
1315         return singleton_->pimpl_->files_to_load_;
1316 }
1317
1318
1319 BufferList & theBufferList()
1320 {
1321         LAPPERR(singleton_);
1322         return singleton_->pimpl_->buffer_list_;
1323 }
1324
1325
1326 Server & theServer()
1327 {
1328         // FIXME: this should not be use_gui dependent
1329         LWARNIF(use_gui);
1330         LAPPERR(singleton_);
1331         return *singleton_->pimpl_->lyx_server_.get();
1332 }
1333
1334
1335 ServerSocket & theServerSocket()
1336 {
1337         // FIXME: this should not be use_gui dependent
1338         LWARNIF(use_gui);
1339         LAPPERR(singleton_);
1340         return *singleton_->pimpl_->lyx_socket_.get();
1341 }
1342
1343
1344 KeyMap & theTopLevelKeymap()
1345 {
1346         LAPPERR(singleton_);
1347         return singleton_->pimpl_->toplevel_keymap_;
1348 }
1349
1350
1351 Converters & theConverters()
1352 {
1353         LAPPERR(singleton_);
1354         return  singleton_->pimpl_->converters_;
1355 }
1356
1357
1358 Converters & theSystemConverters()
1359 {
1360         LAPPERR(singleton_);
1361         return  singleton_->pimpl_->system_converters_;
1362 }
1363
1364
1365 Movers & theMovers()
1366 {
1367         LAPPERR(singleton_);
1368         return singleton_->pimpl_->movers_;
1369 }
1370
1371
1372 Mover const & getMover(string  const & fmt)
1373 {
1374         LAPPERR(singleton_);
1375         return singleton_->pimpl_->movers_(fmt);
1376 }
1377
1378
1379 void setMover(string const & fmt, string const & command)
1380 {
1381         LAPPERR(singleton_);
1382         singleton_->pimpl_->movers_.set(fmt, command);
1383 }
1384
1385
1386 Movers & theSystemMovers()
1387 {
1388         LAPPERR(singleton_);
1389         return singleton_->pimpl_->system_movers_;
1390 }
1391
1392
1393 Messages const & getMessages(string const & language)
1394 {
1395         LAPPERR(singleton_);
1396         return singleton_->messages(language);
1397 }
1398
1399
1400 Messages const & getGuiMessages()
1401 {
1402         LAPPERR(singleton_);
1403         return singleton_->messages(Messages::guiLanguage());
1404 }
1405
1406
1407 Session & theSession()
1408 {
1409         LAPPERR(singleton_);
1410         return *singleton_->pimpl_->session_.get();
1411 }
1412
1413
1414 LaTeXFonts & theLaTeXFonts()
1415 {
1416         LAPPERR(singleton_);
1417         if (!singleton_->pimpl_->latexfonts_)
1418                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1419         return *singleton_->pimpl_->latexfonts_;
1420 }
1421
1422
1423 CmdDef & theTopLevelCmdDef()
1424 {
1425         LAPPERR(singleton_);
1426         return singleton_->pimpl_->toplevel_cmddef_;
1427 }
1428
1429
1430 SpellChecker * theSpellChecker()
1431 {
1432         if (!singleton_->pimpl_->spell_checker_)
1433                 setSpellChecker();
1434         return singleton_->pimpl_->spell_checker_;
1435 }
1436
1437
1438 void setSpellChecker()
1439 {
1440         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1441                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1442
1443         if (lyxrc.spellchecker == "native") {
1444 #if defined(USE_MACOSX_PACKAGING)
1445                 if (!singleton_->pimpl_->apple_spell_checker_)
1446                         singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1447                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1448 #else
1449                 singleton_->pimpl_->spell_checker_ = 0;
1450 #endif
1451         } else if (lyxrc.spellchecker == "aspell") {
1452 #if defined(USE_ASPELL)
1453                 if (!singleton_->pimpl_->aspell_checker_)
1454                         singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1455                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1456 #else
1457                 singleton_->pimpl_->spell_checker_ = 0;
1458 #endif
1459         } else if (lyxrc.spellchecker == "enchant") {
1460 #if defined(USE_ENCHANT)
1461                 if (!singleton_->pimpl_->enchant_checker_)
1462                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1463                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1464 #else
1465                 singleton_->pimpl_->spell_checker_ = 0;
1466 #endif
1467         } else if (lyxrc.spellchecker == "hunspell") {
1468 #if defined(USE_HUNSPELL)
1469                 if (!singleton_->pimpl_->hunspell_checker_)
1470                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1471                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1472 #else
1473                 singleton_->pimpl_->spell_checker_ = 0;
1474 #endif
1475         } else {
1476                 singleton_->pimpl_->spell_checker_ = 0;
1477         }
1478         if (singleton_->pimpl_->spell_checker_) {
1479                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1480                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1481         }
1482 }
1483
1484 } // namespace lyx