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