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