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