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