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