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