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