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