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