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