]> git.lyx.org Git - lyx.git/blob - src/lyx_main.C
src/support/unlink.C: Fix an #include error
[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]))),
484                         "lyx", support::allow_unreadable));
485         }
486
487         if (first_start)
488                 pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
489
490         return EXIT_SUCCESS;
491 }
492
493
494 void LyX::loadFiles()
495 {
496         vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
497         vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
498
499         for (; it != end; ++it) {
500                 if (it->empty())
501                         continue;
502
503                 Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
504                 if (loadLyXFile(buf, *it)) {
505                         ErrorList const & el = buf->errorList("Parse");
506                         if (!el.empty())
507                                 for_each(el.begin(), el.end(),
508                                 boost::bind(&LyX::printError, this, _1));
509                 }
510                 else
511                         pimpl_->buffer_list_.release(buf);
512         }
513 }
514
515
516 void LyX::execBatchCommands()
517 {
518         // The advantage of doing this here is that the event loop
519         // is already started. So any need for interaction will be
520         // aknowledged.
521         restoreGuiSession();
522
523         // Execute batch commands if available
524         if (batch_command.empty())
525                 return;
526
527         lyxerr[Debug::INIT] << "About to handle -x '"
528                 << batch_command << '\'' << endl;
529
530         pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
531 }
532
533
534 void LyX::restoreGuiSession()
535 {
536         LyXView * view = newLyXView();
537
538         // if some files were specified at command-line we assume that the
539         // user wants to edit *these* files and not to restore the session.
540         if (!pimpl_->files_to_load_.empty()) {
541                 for_each(pimpl_->files_to_load_.begin(),
542                         pimpl_->files_to_load_.end(),
543                         bind(&LyXView::loadLyXFile, view, _1, true));
544                 // clear this list to save a few bytes of RAM
545                 pimpl_->files_to_load_.clear();
546                 pimpl_->session_->lastOpened().clear();
547                 return;
548         }
549
550         if (!lyxrc.load_session)
551                 return;
552
553         vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
554         // do not add to the lastfile list since these files are restored from
555         // last session, and should be already there (regular files), or should
556         // not be added at all (help files).
557         for_each(lastopened.begin(), lastopened.end(),
558                 bind(&LyXView::loadLyXFile, view, _1, false));
559
560         // clear this list to save a few bytes of RAM
561         pimpl_->session_->lastOpened().clear();
562 }
563
564
565 LyXView * LyX::newLyXView()
566 {
567         if (!lyx::use_gui)
568                 return 0;
569
570         // determine windows size and position, from lyxrc and/or session
571         // initial geometry
572         unsigned int width = 690;
573         unsigned int height = 510;
574         // default icon size, will be overwritten by  stored session value
575         unsigned int iconSizeXY = 0;
576         bool maximize = false;
577         // first try lyxrc
578         if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
579                 width = lyxrc.geometry_width;
580                 height = lyxrc.geometry_height;
581         }
582         // if lyxrc returns (0,0), then use session info
583         else {
584                 string val = session().sessionInfo().load("WindowWidth");
585                 if (!val.empty())
586                         width = convert<unsigned int>(val);
587                 val = session().sessionInfo().load("WindowHeight");
588                 if (!val.empty())
589                         height = convert<unsigned int>(val);
590                 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
591                         maximize = true;
592                 val = session().sessionInfo().load("IconSizeXY");
593                 if (!val.empty())
594                         iconSizeXY = convert<unsigned int>(val);
595         }
596
597         // if user wants to restore window position
598         int posx = -1;
599         int posy = -1;
600         if (lyxrc.geometry_xysaved) {
601                 string val = session().sessionInfo().load("WindowPosX");
602                 if (!val.empty())
603                         posx = convert<int>(val);
604                 val = session().sessionInfo().load("WindowPosY");
605                 if (!val.empty())
606                         posy = convert<int>(val);
607         }
608
609         if (!geometryArg.empty()) 
610         {
611                 width = 0;
612                 height = 0;
613         }
614
615         // create the main window
616         LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
617
618         return view;
619 }
620
621 /*
622 Signals and Windows
623 ===================
624 The SIGHUP signal does not exist on Windows and does not need to be handled.
625
626 Windows handles SIGFPE and SIGSEGV signals as expected.
627
628 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
629 cause a new thread to be spawned. This may well result in unexpected
630 behaviour by the single-threaded LyX.
631
632 SIGTERM signals will come only from another process actually sending
633 that signal using 'raise' in Windows' POSIX compatability layer. It will
634 not come from the general "terminate process" methods that everyone
635 actually uses (and which can't be trapped). Killing an app 'politely' on
636 Windows involves first sending a WM_CLOSE message, something that is
637 caught already by the Qt frontend.
638
639 For more information see:
640
641 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
642 ...signals are mostly useless on Windows for a variety of reasons that are
643 Windows specific...
644
645 'UNIX Application Migration Guide, Chapter 9'
646 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
647
648 'How To Terminate an Application "Cleanly" in Win32'
649 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
650 */
651 extern "C" {
652
653 static void error_handler(int err_sig)
654 {
655         // Throw away any signals other than the first one received.
656         static sig_atomic_t handling_error = false;
657         if (handling_error)
658                 return;
659         handling_error = true;
660
661         // We have received a signal indicating a fatal error, so
662         // try and save the data ASAP.
663         LyX::cref().emergencyCleanup();
664
665         // These lyxerr calls may or may not work:
666
667         // Signals are asynchronous, so the main program may be in a very
668         // fragile state when a signal is processed and thus while a signal
669         // handler function executes.
670         // In general, therefore, we should avoid performing any
671         // I/O operations or calling most library and system functions from
672         // signal handlers.
673
674         // This shouldn't matter here, however, as we've already invoked
675         // emergencyCleanup.
676         switch (err_sig) {
677 #ifdef SIGHUP
678         case SIGHUP:
679                 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
680                 break;
681 #endif
682         case SIGFPE:
683                 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
684                 break;
685         case SIGSEGV:
686                 lyxerr << "\nlyx: SIGSEGV signal caught\n"
687                           "Sorry, you have found a bug in LyX. "
688                           "Please read the bug-reporting instructions "
689                           "in Help->Introduction and send us a bug report, "
690                           "if necessary. Thanks !\nBye." << endl;
691                 break;
692         case SIGINT:
693         case SIGTERM:
694                 // no comments
695                 break;
696         }
697
698         // Deinstall the signal handlers
699 #ifdef SIGHUP
700         signal(SIGHUP, SIG_DFL);
701 #endif
702         signal(SIGINT, SIG_DFL);
703         signal(SIGFPE, SIG_DFL);
704         signal(SIGSEGV, SIG_DFL);
705         signal(SIGTERM, SIG_DFL);
706
707 #ifdef SIGHUP
708         if (err_sig == SIGSEGV ||
709             (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
710 #else
711         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
712 #endif
713                 support::abort();
714         exit(0);
715 }
716
717 }
718
719
720 void LyX::printError(ErrorItem const & ei)
721 {
722         docstring tmp = _("LyX: ") + ei.error + char_type(':')
723                 + ei.description;
724         std::cerr << to_utf8(tmp) << std::endl;
725 }
726
727
728 void LyX::initGuiFont()
729 {
730         if (lyxrc.roman_font_name.empty())
731                 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
732
733         if (lyxrc.sans_font_name.empty())
734                 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
735
736         if (lyxrc.typewriter_font_name.empty())
737                 lyxrc.typewriter_font_name 
738                         = pimpl_->application_->typewriterFontName();
739 }
740
741
742 bool LyX::init()
743 {
744 #ifdef SIGHUP
745         signal(SIGHUP, error_handler);
746 #endif
747         signal(SIGFPE, error_handler);
748         signal(SIGSEGV, error_handler);
749         signal(SIGINT, error_handler);
750         signal(SIGTERM, error_handler);
751         // SIGPIPE can be safely ignored.
752
753         lyxrc.tempdir_path = package().temp_dir();
754         lyxrc.document_path = package().document_dir();
755
756         if (lyxrc.template_path.empty()) {
757                 lyxrc.template_path = addPath(package().system_support(),
758                                               "templates");
759         }
760
761         //
762         // Read configuration files
763         //
764
765         // This one may have been distributed along with LyX.
766         if (!readRcFile("lyxrc.dist"))
767                 return false;
768
769         // Set the PATH correctly.
770 #if !defined (USE_POSIX_PACKAGING)
771         // Add the directory containing the LyX executable to the path
772         // so that LyX can find things like tex2lyx.
773         if (package().build_support().empty())
774                 prependEnvPath("PATH", package().binary_dir());
775 #endif
776         if (!lyxrc.path_prefix.empty())
777                 prependEnvPath("PATH", lyxrc.path_prefix);
778
779         // Check that user LyX directory is ok.
780         if (queryUserLyXDir(package().explicit_user_support()))
781                 reconfigureUserLyXDir();
782
783         // no need for a splash when there is no GUI
784         if (!use_gui) {
785                 first_start = false;
786         }
787
788         // This one is generated in user_support directory by lib/configure.py.
789         if (!readRcFile("lyxrc.defaults"))
790                 return false;
791
792         // Query the OS to know what formats are viewed natively
793         formats.setAutoOpen();
794
795         // Read lyxrc.dist again to be able to override viewer auto-detection.
796         readRcFile("lyxrc.dist");
797
798         system_lyxrc = lyxrc;
799         system_formats = formats;
800         system_converters = converters;
801         system_movers = movers;
802         system_lcolor = lcolor;
803
804         // This one is edited through the preferences dialog.
805         if (!readRcFile("preferences"))
806                 return false;
807
808         if (!readEncodingsFile("encodings"))
809                 return false;
810         if (!readLanguagesFile("languages"))
811                 return false;
812
813         // Load the layouts
814         lyxerr[Debug::INIT] << "Reading layouts..." << endl;
815         if (!LyXSetStyle())
816                 return false;
817
818         if (use_gui) {
819                 // Set up bindings
820                 pimpl_->toplevel_keymap_.reset(new kb_keymap);
821                 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
822                 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
823
824                 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
825
826                 // Read menus
827                 if (!readUIFile(lyxrc.ui_file))
828                         return false;
829         }
830
831         if (lyxerr.debugging(Debug::LYXRC))
832                 lyxrc.print();
833
834         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
835         if (!lyxrc.path_prefix.empty())
836                 prependEnvPath("PATH", lyxrc.path_prefix);
837
838         FileName const document_path(lyxrc.document_path);
839         if (fs::exists(document_path.toFilesystemEncoding()) &&
840             fs::is_directory(document_path.toFilesystemEncoding()))
841                 package().document_dir() = lyxrc.document_path;
842
843         package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
844         if (package().temp_dir().empty()) {
845                 Alert::error(_("Could not create temporary directory"),
846                              bformat(_("Could not create a temporary directory in\n"
847                                                     "%1$s. Make sure that this\n"
848                                                     "path exists and is writable and try again."),
849                                      from_utf8(lyxrc.tempdir_path)));
850                 // createLyXTmpDir() tries sufficiently hard to create a
851                 // usable temp dir, so the probability to come here is
852                 // close to zero. We therefore don't try to overcome this
853                 // problem with e.g. asking the user for a new path and
854                 // trying again but simply exit.
855                 return false;
856         }
857
858         if (lyxerr.debugging(Debug::INIT)) {
859                 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
860         }
861
862         lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
863         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
864
865         // This must happen after package initialization and after lyxrc is
866         // read, therefore it can't be done by a static object.
867         ConverterCache::init();
868
869         return true;
870 }
871
872
873 void LyX::defaultKeyBindings(kb_keymap  * kbmap)
874 {
875         kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
876         kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
877         kbmap->bind("Up", FuncRequest(LFUN_UP));
878         kbmap->bind("Down", FuncRequest(LFUN_DOWN));
879
880         kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
881         kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
882         kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
883         kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
884
885         kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
886         kbmap->bind("End", FuncRequest(LFUN_LINE_END));
887         kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
888         kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
889
890         kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
891         //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
892
893         kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
894         kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
895
896         // kbmap->bindings to enable the use of the numeric keypad
897         // e.g. Num Lock set
898         //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
899         //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
900         kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
901         //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
902         //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
903         //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
904         //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
905         //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
906         //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
907         //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
908         //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
909         //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
910         //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
911         //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
912         //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
913         //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
914         kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
915         kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
916         kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
917         kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
918         kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
919         kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
920         kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
921         kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
922 }
923
924
925 void LyX::emergencyCleanup() const
926 {
927         // what to do about tmpfiles is non-obvious. we would
928         // like to delete any we find, but our lyxdir might
929         // contain documents etc. which might be helpful on
930         // a crash
931
932         pimpl_->buffer_list_.emergencyWriteAll();
933         if (use_gui) {
934                 if (pimpl_->lyx_server_)
935                         pimpl_->lyx_server_->emergencyCleanup();
936                 pimpl_->lyx_server_.reset();
937                 pimpl_->lyx_socket_.reset();
938         }
939 }
940
941
942 void LyX::deadKeyBindings(kb_keymap * kbmap)
943 {
944         // bindKeyings for transparent handling of deadkeys
945         // The keysyms are gotten from XFree86 X11R6
946         kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
947         kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
948         kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
949         kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
950         kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
951         kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
952         kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
953         kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
954         kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
955         kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
956         // nothing with this name
957         // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
958         kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
959         kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
960         // nothing with this name either...
961         //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
962         kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
963         kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
964         kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
965 }
966
967
968 namespace {
969
970 // return true if file does not exist or is older than configure.py.
971 bool needsUpdate(string const & file)
972 {
973         // We cannot initialize configure_script directly because the package
974         // is not initialized yet when  static objects are constructed.
975         static string configure_script;
976         static bool firstrun = true;
977         if (firstrun) {
978                 configure_script = FileName(addName(
979                                 package().system_support(),
980                                 "configure.py")).toFilesystemEncoding();
981                 firstrun = false;
982         }
983
984         string const absfile = FileName(addName(
985                 package().user_support(), file)).toFilesystemEncoding();
986         return (! fs::exists(absfile))
987                 || (fs::last_write_time(configure_script)
988                     > fs::last_write_time(absfile));
989 }
990
991 }
992
993
994 bool LyX::queryUserLyXDir(bool explicit_userdir)
995 {
996         // Does user directory exist?
997         string const user_support =
998                 FileName(package().user_support()).toFilesystemEncoding();
999         if (fs::exists(user_support) && fs::is_directory(user_support)) {
1000                 first_start = false;
1001
1002                 return needsUpdate("lyxrc.defaults")
1003                         || needsUpdate("textclass.lst")
1004                         || needsUpdate("packages.lst");
1005         }
1006
1007         first_start = !explicit_userdir;
1008
1009         // If the user specified explicitly a directory, ask whether
1010         // to create it. If the user says "no", then exit.
1011         if (explicit_userdir &&
1012             Alert::prompt(
1013                     _("Missing user LyX directory"),
1014                     bformat(_("You have specified a non-existent user "
1015                                            "LyX directory, %1$s.\n"
1016                                            "It is needed to keep your own configuration."),
1017                             from_utf8(package().user_support())),
1018                     1, 0,
1019                     _("&Create directory"),
1020                     _("&Exit LyX"))) {
1021                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1022                 earlyExit(EXIT_FAILURE);
1023         }
1024
1025         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1026                           from_utf8(package().user_support())))
1027                << endl;
1028
1029         if (!createDirectory(package().user_support(), 0755)) {
1030                 // Failed, so let's exit.
1031                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1032                        << endl;
1033                 earlyExit(EXIT_FAILURE);
1034         }
1035
1036         return true;
1037 }
1038
1039
1040 bool LyX::readRcFile(string const & name)
1041 {
1042         lyxerr[Debug::INIT] << "About to read " << name << "... ";
1043
1044         FileName const lyxrc_path = libFileSearch(string(), name);
1045         if (!lyxrc_path.empty()) {
1046
1047                 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1048
1049                 if (lyxrc.read(lyxrc_path) < 0) {
1050                         showFileError(name);
1051                         return false;
1052                 }
1053         } else
1054                 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1055         return true;
1056
1057 }
1058
1059
1060 // Read the ui file `name'
1061 bool LyX::readUIFile(string const & name)
1062 {
1063         enum Uitags {
1064                 ui_menuset = 1,
1065                 ui_toolbar,
1066                 ui_toolbars,
1067                 ui_include,
1068                 ui_last
1069         };
1070
1071         struct keyword_item uitags[ui_last - 1] = {
1072                 { "include", ui_include },
1073                 { "menuset", ui_menuset },
1074                 { "toolbar", ui_toolbar },
1075                 { "toolbars", ui_toolbars }
1076         };
1077
1078         // Ensure that a file is read only once (prevents include loops)
1079         static std::list<string> uifiles;
1080         std::list<string>::const_iterator it  = uifiles.begin();
1081         std::list<string>::const_iterator end = uifiles.end();
1082         it = std::find(it, end, name);
1083         if (it != end) {
1084                 lyxerr[Debug::INIT] << "UI file '" << name
1085                                     << "' has been read already. "
1086                                     << "Is this an include loop?"
1087                                     << endl;
1088                 return false;
1089         }
1090
1091         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1092
1093         FileName const ui_path = libFileSearch("ui", name, "ui");
1094
1095         if (ui_path.empty()) {
1096                 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1097                 showFileError(name);
1098                 return false;
1099         }
1100         uifiles.push_back(name);
1101
1102         lyxerr[Debug::INIT] << "Found " << name
1103                             << " in " << ui_path << endl;
1104         LyXLex lex(uitags, ui_last - 1);
1105         lex.setFile(ui_path);
1106         if (!lex.isOK()) {
1107                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1108                        << endl;
1109         }
1110
1111         if (lyxerr.debugging(Debug::PARSER))
1112                 lex.printTable(lyxerr);
1113
1114         while (lex.isOK()) {
1115                 switch (lex.lex()) {
1116                 case ui_include: {
1117                         lex.next(true);
1118                         string const file = lex.getString();
1119                         if (!readUIFile(file))
1120                                 return false;
1121                         break;
1122                 }
1123                 case ui_menuset:
1124                         menubackend.read(lex);
1125                         break;
1126
1127                 case ui_toolbar:
1128                         toolbarbackend.read(lex);
1129                         break;
1130
1131                 case ui_toolbars:
1132                         toolbarbackend.readToolbars(lex);
1133                         break;
1134
1135                 default:
1136                         if (!rtrim(lex.getString()).empty())
1137                                 lex.printError("LyX::ReadUIFile: "
1138                                                "Unknown menu tag: `$$Token'");
1139                         break;
1140                 }
1141         }
1142         return true;
1143 }
1144
1145
1146 // Read the languages file `name'
1147 bool LyX::readLanguagesFile(string const & name)
1148 {
1149         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1150
1151         FileName const lang_path = libFileSearch(string(), name);
1152         if (lang_path.empty()) {
1153                 showFileError(name);
1154                 return false;
1155         }
1156         languages.read(lang_path);
1157         return true;
1158 }
1159
1160
1161 // Read the encodings file `name'
1162 bool LyX::readEncodingsFile(string const & name)
1163 {
1164         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1165
1166         FileName const enc_path = libFileSearch(string(), name);
1167         if (enc_path.empty()) {
1168                 showFileError(name);
1169                 return false;
1170         }
1171         encodings.read(enc_path);
1172         return true;
1173 }
1174
1175
1176 namespace {
1177
1178 string batch;
1179
1180 /// return the the number of arguments consumed
1181 typedef boost::function<int(string const &, string const &)> cmd_helper;
1182
1183 int parse_dbg(string const & arg, string const &)
1184 {
1185         if (arg.empty()) {
1186                 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1187                 Debug::showTags(lyxerr);
1188                 exit(0);
1189         }
1190         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1191
1192         lyxerr.level(Debug::value(arg));
1193         Debug::showLevel(lyxerr, lyxerr.level());
1194         return 1;
1195 }
1196
1197
1198 int parse_help(string const &, string const &)
1199 {
1200         lyxerr <<
1201                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1202                   "Command line switches (case sensitive):\n"
1203                   "\t-help              summarize LyX usage\n"
1204                   "\t-userdir dir       set user directory to dir\n"
1205                   "\t-sysdir dir        set system directory to dir\n"
1206                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1207                   "\t-dbg feature[,feature]...\n"
1208                   "                  select the features to debug.\n"
1209                   "                  Type `lyx -dbg' to see the list of features\n"
1210                   "\t-x [--execute] command\n"
1211                   "                  where command is a lyx command.\n"
1212                   "\t-e [--export] fmt\n"
1213                   "                  where fmt is the export format of choice.\n"
1214                   "\t-i [--import] fmt file.xxx\n"
1215                   "                  where fmt is the import format of choice\n"
1216                   "                  and file.xxx is the file to be imported.\n"
1217                   "\t-version        summarize version and build info\n"
1218                                "Check the LyX man page for more details.")) << endl;
1219         exit(0);
1220         return 0;
1221 }
1222
1223 int parse_version(string const &, string const &)
1224 {
1225         lyxerr << "LyX " << lyx_version
1226                << " (" << lyx_release_date << ")" << endl;
1227         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1228
1229         lyxerr << lyx_version_info << endl;
1230         exit(0);
1231         return 0;
1232 }
1233
1234 int parse_sysdir(string const & arg, string const &)
1235 {
1236         if (arg.empty()) {
1237                 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1238                 exit(1);
1239         }
1240         cl_system_support = arg;
1241         return 1;
1242 }
1243
1244 int parse_userdir(string const & arg, string const &)
1245 {
1246         if (arg.empty()) {
1247                 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1248                 exit(1);
1249         }
1250         cl_user_support = arg;
1251         return 1;
1252 }
1253
1254 int parse_execute(string const & arg, string const &)
1255 {
1256         if (arg.empty()) {
1257                 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1258                 exit(1);
1259         }
1260         batch = arg;
1261         return 1;
1262 }
1263
1264 int parse_export(string const & type, string const &)
1265 {
1266         if (type.empty()) {
1267                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1268                                          "--export switch")) << endl;
1269                 exit(1);
1270         }
1271         batch = "buffer-export " + type;
1272         use_gui = false;
1273         return 1;
1274 }
1275
1276 int parse_import(string const & type, string const & file)
1277 {
1278         if (type.empty()) {
1279                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1280                                          "--import switch")) << endl;
1281                 exit(1);
1282         }
1283         if (file.empty()) {
1284                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1285                 exit(1);
1286         }
1287
1288         batch = "buffer-import " + type + ' ' + file;
1289         return 2;
1290 }
1291
1292 int parse_geometry(string const & arg1, string const &)
1293 {
1294         geometryArg = arg1;
1295 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1296         // remove also the arg
1297         return 1;
1298 #else
1299         // don't remove "-geometry"
1300         return -1;
1301 #endif
1302 }
1303
1304
1305 } // namespace anon
1306
1307
1308 void LyX::easyParse(int & argc, char * argv[])
1309 {
1310         std::map<string, cmd_helper> cmdmap;
1311
1312         cmdmap["-dbg"] = parse_dbg;
1313         cmdmap["-help"] = parse_help;
1314         cmdmap["--help"] = parse_help;
1315         cmdmap["-version"] = parse_version;
1316         cmdmap["--version"] = parse_version;
1317         cmdmap["-sysdir"] = parse_sysdir;
1318         cmdmap["-userdir"] = parse_userdir;
1319         cmdmap["-x"] = parse_execute;
1320         cmdmap["--execute"] = parse_execute;
1321         cmdmap["-e"] = parse_export;
1322         cmdmap["--export"] = parse_export;
1323         cmdmap["-i"] = parse_import;
1324         cmdmap["--import"] = parse_import;
1325         cmdmap["-geometry"] = parse_geometry;
1326
1327         for (int i = 1; i < argc; ++i) {
1328                 std::map<string, cmd_helper>::const_iterator it
1329                         = cmdmap.find(argv[i]);
1330
1331                 // don't complain if not found - may be parsed later
1332                 if (it == cmdmap.end())
1333                         continue;
1334
1335                 string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
1336                 string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
1337
1338                 int const remove = 1 + it->second(arg, arg2);
1339
1340                 // Now, remove used arguments by shifting
1341                 // the following ones remove places down.
1342                 if (remove > 0) {
1343                         argc -= remove;
1344                         for (int j = i; j < argc; ++j)
1345                                 argv[j] = argv[j + remove];
1346                         --i;
1347                 }
1348         }
1349
1350         batch_command = batch;
1351 }
1352
1353
1354 FuncStatus getStatus(FuncRequest const & action)
1355 {
1356         return LyX::ref().lyxFunc().getStatus(action);
1357 }
1358
1359
1360 void dispatch(FuncRequest const & action)
1361 {
1362         LyX::ref().lyxFunc().dispatch(action);
1363 }
1364
1365
1366 BufferList & theBufferList()
1367 {
1368         return LyX::ref().bufferList();
1369 }
1370
1371
1372 LyXFunc & theLyXFunc()
1373 {
1374         return LyX::ref().lyxFunc();
1375 }
1376
1377
1378 LyXServer & theLyXServer()
1379 {
1380         // FIXME: this should not be use_gui dependent
1381         BOOST_ASSERT(use_gui);
1382         return LyX::ref().server();
1383 }
1384
1385
1386 LyXServerSocket & theLyXServerSocket()
1387 {
1388         // FIXME: this should not be use_gui dependent
1389         BOOST_ASSERT(use_gui);
1390         return LyX::ref().socket();
1391 }
1392
1393
1394 kb_keymap & theTopLevelKeymap()
1395 {
1396         BOOST_ASSERT(use_gui);
1397         return LyX::ref().topLevelKeymap();
1398 }
1399
1400
1401 IconvProcessor & utf8ToUcs4()
1402 {
1403         return LyX::ref().iconvProcessor();
1404 }
1405
1406 } // namespace lyx