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