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