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