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