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