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