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