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