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