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