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