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