]> git.lyx.org Git - lyx.git/blob - src/lyx_main.C
a9342a777c30aebce9bb7aa35c616c2638b9632f
[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 "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         if (lyx::use_gui)
760                 application_->server().emergencyCleanup();
761 }
762
763
764 void LyX::deadKeyBindings(kb_keymap * kbmap)
765 {
766         // bindKeyings for transparent handling of deadkeys
767         // The keysyms are gotten from XFree86 X11R6
768         kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
769         kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
770         kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
771         kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
772         kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
773         kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
774         kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
775         kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
776         kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
777         kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
778         // nothing with this name
779         // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
780         kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
781         kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
782         // nothing with this name either...
783         //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
784         kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
785         kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
786         kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
787 }
788
789
790 namespace {
791
792 // return true if file does not exist or is older than configure.py.
793 bool needsUpdate(string const & file)
794 {
795         static string const configure_script =
796                 addName(package().system_support(), "configure.py");
797         string const absfile =
798                 addName(package().user_support(), file);
799
800         return (! fs::exists(absfile))
801                 || (fs::last_write_time(configure_script)
802                     > fs::last_write_time(absfile));
803 }
804
805 }
806
807
808 bool LyX::queryUserLyXDir(bool explicit_userdir)
809 {
810         // Does user directory exist?
811         if (fs::exists(package().user_support()) &&
812             fs::is_directory(package().user_support())) {
813                 first_start = false;
814
815                 return needsUpdate("lyxrc.defaults")
816                         || needsUpdate("textclass.lst")
817                         || needsUpdate("packages.lst");
818         }
819
820         first_start = !explicit_userdir;
821
822         // If the user specified explicitly a directory, ask whether
823         // to create it. If the user says "no", then exit.
824         if (explicit_userdir &&
825             Alert::prompt(
826                     _("Missing user LyX directory"),
827                     bformat(_("You have specified a non-existent user "
828                                            "LyX directory, %1$s.\n"
829                                            "It is needed to keep your own configuration."),
830                             lyx::from_utf8(package().user_support())),
831                     1, 0,
832                     _("&Create directory"),
833                     _("&Exit LyX"))) {
834                 lyxerr << lyx::to_utf8(_("No user LyX directory. Exiting.")) << endl;
835                 earlyExit(EXIT_FAILURE);
836         }
837
838         lyxerr << lyx::to_utf8(bformat(_("LyX: Creating directory %1$s"),
839                           lyx::from_utf8(package().user_support())))
840                << endl;
841
842         if (!createDirectory(package().user_support(), 0755)) {
843                 // Failed, so let's exit.
844                 lyxerr << lyx::to_utf8(_("Failed to create directory. Exiting."))
845                        << endl;
846                 earlyExit(EXIT_FAILURE);
847         }
848
849         return true;
850 }
851
852
853 bool LyX::readRcFile(string const & name)
854 {
855         lyxerr[Debug::INIT] << "About to read " << name << "... ";
856
857         string const lyxrc_path = libFileSearch(string(), name);
858         if (!lyxrc_path.empty()) {
859
860                 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
861
862                 if (lyxrc.read(lyxrc_path) < 0) {
863                         showFileError(name);
864                         return false;
865                 }
866         } else
867                 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
868         return true;
869
870 }
871
872
873 // Read the ui file `name'
874 bool LyX::readUIFile(string const & name)
875 {
876         enum Uitags {
877                 ui_menuset = 1,
878                 ui_toolbar,
879                 ui_toolbars,
880                 ui_include,
881                 ui_last
882         };
883
884         struct keyword_item uitags[ui_last - 1] = {
885                 { "include", ui_include },
886                 { "menuset", ui_menuset },
887                 { "toolbar", ui_toolbar },
888                 { "toolbars", ui_toolbars }
889         };
890
891         // Ensure that a file is read only once (prevents include loops)
892         static std::list<string> uifiles;
893         std::list<string>::const_iterator it  = uifiles.begin();
894         std::list<string>::const_iterator end = uifiles.end();
895         it = std::find(it, end, name);
896         if (it != end) {
897                 lyxerr[Debug::INIT] << "UI file '" << name
898                                     << "' has been read already. "
899                                     << "Is this an include loop?"
900                                     << endl;
901                 return false;
902         }
903
904         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
905
906         string const ui_path = libFileSearch("ui", name, "ui");
907
908         if (ui_path.empty()) {
909                 lyxerr[Debug::INIT] << "Could not find " << name << endl;
910                 showFileError(name);
911                 return false;
912         }
913         uifiles.push_back(name);
914
915         lyxerr[Debug::INIT] << "Found " << name
916                             << " in " << ui_path << endl;
917         LyXLex lex(uitags, ui_last - 1);
918         lex.setFile(ui_path);
919         if (!lex.isOK()) {
920                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
921                        << endl;
922         }
923
924         if (lyxerr.debugging(Debug::PARSER))
925                 lex.printTable(lyxerr);
926
927         while (lex.isOK()) {
928                 switch (lex.lex()) {
929                 case ui_include: {
930                         lex.next(true);
931                         string const file = lex.getString();
932                         if (!readUIFile(file))
933                                 return false;
934                         break;
935                 }
936                 case ui_menuset:
937                         menubackend.read(lex);
938                         break;
939
940                 case ui_toolbar:
941                         toolbarbackend.read(lex);
942                         break;
943
944                 case ui_toolbars:
945                         toolbarbackend.readToolbars(lex);
946                         break;
947
948                 default:
949                         if (!rtrim(lex.getString()).empty())
950                                 lex.printError("LyX::ReadUIFile: "
951                                                "Unknown menu tag: `$$Token'");
952                         break;
953                 }
954         }
955         return true;
956 }
957
958
959 // Read the languages file `name'
960 bool LyX::readLanguagesFile(string const & name)
961 {
962         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
963
964         string const lang_path = libFileSearch(string(), name);
965         if (lang_path.empty()) {
966                 showFileError(name);
967                 return false;
968         }
969         languages.read(lang_path);
970         return true;
971 }
972
973
974 // Read the encodings file `name'
975 bool LyX::readEncodingsFile(string const & name)
976 {
977         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
978
979         string const enc_path = libFileSearch(string(), name);
980         if (enc_path.empty()) {
981                 showFileError(name);
982                 return false;
983         }
984         encodings.read(enc_path);
985         return true;
986 }
987
988
989 namespace {
990
991 string batch;
992
993 /// return the the number of arguments consumed
994 typedef boost::function<int(string const &, string const &)> cmd_helper;
995
996 int parse_dbg(string const & arg, string const &)
997 {
998         if (arg.empty()) {
999                 lyxerr << lyx::to_utf8(_("List of supported debug flags:")) << endl;
1000                 Debug::showTags(lyxerr);
1001                 exit(0);
1002         }
1003         lyxerr << lyx::to_utf8(bformat(_("Setting debug level to %1$s"), lyx::from_utf8(arg))) << endl;
1004
1005         lyxerr.level(Debug::value(arg));
1006         Debug::showLevel(lyxerr, lyxerr.level());
1007         return 1;
1008 }
1009
1010
1011 int parse_help(string const &, string const &)
1012 {
1013         lyxerr <<
1014                 lyx::to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1015                   "Command line switches (case sensitive):\n"
1016                   "\t-help              summarize LyX usage\n"
1017                   "\t-userdir dir       set user directory to dir\n"
1018                   "\t-sysdir dir        set system directory to dir\n"
1019                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1020                   "\t-dbg feature[,feature]...\n"
1021                   "                  select the features to debug.\n"
1022                   "                  Type `lyx -dbg' to see the list of features\n"
1023                   "\t-x [--execute] command\n"
1024                   "                  where command is a lyx command.\n"
1025                   "\t-e [--export] fmt\n"
1026                   "                  where fmt is the export format of choice.\n"
1027                   "\t-i [--import] fmt file.xxx\n"
1028                   "                  where fmt is the import format of choice\n"
1029                   "                  and file.xxx is the file to be imported.\n"
1030                   "\t-version        summarize version and build info\n"
1031                                "Check the LyX man page for more details.")) << endl;
1032         exit(0);
1033         return 0;
1034 }
1035
1036 int parse_version(string const &, string const &)
1037 {
1038         lyxerr << "LyX " << lyx_version
1039                << " (" << lyx_release_date << ")" << endl;
1040         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1041
1042         lyxerr << lyx_version_info << endl;
1043         exit(0);
1044         return 0;
1045 }
1046
1047 int parse_sysdir(string const & arg, string const &)
1048 {
1049         if (arg.empty()) {
1050                 lyxerr << lyx::to_utf8(_("Missing directory for -sysdir switch")) << endl;
1051                 exit(1);
1052         }
1053         cl_system_support = arg;
1054         return 1;
1055 }
1056
1057 int parse_userdir(string const & arg, string const &)
1058 {
1059         if (arg.empty()) {
1060                 lyxerr << lyx::to_utf8(_("Missing directory for -userdir switch")) << endl;
1061                 exit(1);
1062         }
1063         cl_user_support = arg;
1064         return 1;
1065 }
1066
1067 int parse_execute(string const & arg, string const &)
1068 {
1069         if (arg.empty()) {
1070                 lyxerr << lyx::to_utf8(_("Missing command string after --execute switch")) << endl;
1071                 exit(1);
1072         }
1073         batch = arg;
1074         return 1;
1075 }
1076
1077 int parse_export(string const & type, string const &)
1078 {
1079         if (type.empty()) {
1080                 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1081                                          "--export switch")) << endl;
1082                 exit(1);
1083         }
1084         batch = "buffer-export " + type;
1085         lyx::use_gui = false;
1086         return 1;
1087 }
1088
1089 int parse_import(string const & type, string const & file)
1090 {
1091         if (type.empty()) {
1092                 lyxerr << lyx::to_utf8(_("Missing file type [eg latex, ps...] after "
1093                                          "--import switch")) << endl;
1094                 exit(1);
1095         }
1096         if (file.empty()) {
1097                 lyxerr << lyx::to_utf8(_("Missing filename for --import")) << endl;
1098                 exit(1);
1099         }
1100
1101         batch = "buffer-import " + type + ' ' + file;
1102         return 2;
1103 }
1104
1105 } // namespace anon
1106
1107
1108 void LyX::easyParse(int & argc, char * argv[])
1109 {
1110         std::map<string, cmd_helper> cmdmap;
1111
1112         cmdmap["-dbg"] = parse_dbg;
1113         cmdmap["-help"] = parse_help;
1114         cmdmap["--help"] = parse_help;
1115         cmdmap["-version"] = parse_version;
1116         cmdmap["--version"] = parse_version;
1117         cmdmap["-sysdir"] = parse_sysdir;
1118         cmdmap["-userdir"] = parse_userdir;
1119         cmdmap["-x"] = parse_execute;
1120         cmdmap["--execute"] = parse_execute;
1121         cmdmap["-e"] = parse_export;
1122         cmdmap["--export"] = parse_export;
1123         cmdmap["-i"] = parse_import;
1124         cmdmap["--import"] = parse_import;
1125
1126         for (int i = 1; i < argc; ++i) {
1127                 std::map<string, cmd_helper>::const_iterator it
1128                         = cmdmap.find(argv[i]);
1129
1130                 // check for X11 -geometry option
1131                 if (lyx::support::compare(argv[i], "-geometry") == 0)
1132                         geometryOption_ = true;
1133
1134                 // don't complain if not found - may be parsed later
1135                 if (it == cmdmap.end())
1136                         continue;
1137
1138                 string arg((i + 1 < argc) ? argv[i + 1] : "");
1139                 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1140
1141                 int const remove = 1 + it->second(arg, arg2);
1142
1143                 // Now, remove used arguments by shifting
1144                 // the following ones remove places down.
1145                 argc -= remove;
1146                 for (int j = i; j < argc; ++j)
1147                         argv[j] = argv[j + remove];
1148                 --i;
1149         }
1150
1151         batch_command = batch;
1152 }