]> git.lyx.org Git - lyx.git/blob - src/lyx_main.C
Fix crash on Mac, now the strange part
[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 "ConverterCache.h"
21 #include "buffer.h"
22 #include "buffer_funcs.h"
23 #include "bufferlist.h"
24 #include "converter.h"
25 #include "debug.h"
26 #include "encoding.h"
27 #include "errorlist.h"
28 #include "format.h"
29 #include "gettext.h"
30 #include "kbmap.h"
31 #include "language.h"
32 #include "session.h"
33 #include "LColor.h"
34 #include "lyx_cb.h"
35 #include "LyXAction.h"
36 #include "lyxfunc.h"
37 #include "lyxlex.h"
38 #include "lyxrc.h"
39 #include "lyxserver.h"
40 #include "lyxsocket.h"
41 #include "lyxtextclasslist.h"
42 #include "MenuBackend.h"
43 #include "mover.h"
44 #include "ToolbarBackend.h"
45
46 #include "frontends/Alert.h"
47 #include "frontends/Application.h"
48 #include "frontends/Gui.h"
49 #include "frontends/LyXView.h"
50
51 #include "support/environment.h"
52 #include "support/filetools.h"
53 #include "support/fontutils.h"
54 #include "support/lyxlib.h"
55 #include "support/convert.h"
56 #include "support/os.h"
57 #include "support/package.h"
58 #include "support/path.h"
59 #include "support/systemcall.h"
60 #include "support/unicode.h"
61
62 #include <boost/bind.hpp>
63 #include <boost/filesystem/operations.hpp>
64
65 #include <iostream>
66 #include <csignal>
67
68
69 namespace lyx {
70
71 using support::addName;
72 using support::addPath;
73 using support::bformat;
74 using support::createDirectory;
75 using support::createLyXTmpDir;
76 using support::destroyDir;
77 using support::FileName;
78 using support::fileSearch;
79 using support::getEnv;
80 using support::i18nLibFileSearch;
81 using support::libFileSearch;
82 using support::package;
83 using support::prependEnvPath;
84 using support::rtrim;
85 using support::Systemcall;
86
87 namespace Alert = frontend::Alert;
88 namespace os = support::os;
89 namespace fs = boost::filesystem;
90
91 using std::endl;
92 using std::string;
93 using std::vector;
94 using std::for_each;
95
96 #ifndef CXX_GLOBAL_CSTD
97 using std::exit;
98 using std::signal;
99 using std::system;
100 #endif
101
102 ///
103 frontend::Application * theApp = 0;
104
105 /// are we using the GUI at all?
106 /** 
107 * We default to true and this is changed to false when the export feature is used.
108 */
109 bool use_gui = true;
110
111
112 namespace {
113
114 // Filled with the command line arguments "foo" of "-sysdir foo" or
115 // "-userdir foo".
116 string cl_system_support;
117 string cl_user_support;
118
119 std::string geometryArg;
120
121 LyX * singleton_ = 0;
122
123 void showFileError(string const & error)
124 {
125         Alert::warning(_("Could not read configuration file"),
126                        bformat(_("Error while reading the configuration file\n%1$s.\n"
127                            "Please check your installation."), from_utf8(error)));
128 }
129
130
131 void reconfigureUserLyXDir()
132 {
133         string const configure_command = package().configure_command();
134
135         lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
136         support::Path p(package().user_support());
137         Systemcall one;
138         one.startscript(Systemcall::Wait, configure_command);
139         lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
140 }
141
142 } // namespace anon
143
144
145 /// The main application class private implementation.
146 struct LyX::Singletons 
147 {
148         Singletons(): iconv(ucs4_codeset, "UTF-8")
149         {
150         }
151         /// our function handler
152         LyXFunc lyxfunc_;
153         ///
154         BufferList buffer_list_;
155         ///
156         boost::scoped_ptr<kb_keymap> toplevel_keymap_;
157         ///
158         boost::scoped_ptr<LyXServer> lyx_server_;
159         ///
160         boost::scoped_ptr<LyXServerSocket> lyx_socket_;
161         ///
162         boost::scoped_ptr<frontend::Application> application_;
163         /// lyx session, containing lastfiles, lastfilepos, and lastopened
164         boost::scoped_ptr<Session> session_;
165
166         ///
167         IconvProcessor iconv;
168 };
169
170
171 LyX::~LyX()
172 {
173         // Static data are not treated in the same way at all on the Mac (and
174         // the LyX singleton has static methods). This is the reason why the
175         // exit command on the Mac bypasses our dispatch machinery altogether.
176         // On Linux and Windows we won't pass a second time through quit()
177         // because quitting will already be set to true.
178         if (!quitting)
179                 quit();
180 }
181
182
183 LyX & LyX::ref()
184 {
185         BOOST_ASSERT(singleton_);
186         return *singleton_;
187 }
188
189
190 LyX const & LyX::cref()
191 {
192         BOOST_ASSERT(singleton_);
193         return *singleton_;
194 }
195
196
197 LyX::LyX()
198         : first_start(false)
199 {
200         singleton_ = this;
201         pimpl_.reset(new Singletons);
202         geometryArg.clear();
203 }
204
205
206 BufferList & LyX::bufferList()
207 {
208         return pimpl_->buffer_list_;
209 }
210
211
212 BufferList const & LyX::bufferList() const
213 {
214         return pimpl_->buffer_list_;
215 }
216
217
218 Session & LyX::session()
219 {
220         BOOST_ASSERT(pimpl_->session_.get());
221         return *pimpl_->session_.get();
222 }
223
224
225 Session const & LyX::session() const
226 {
227         BOOST_ASSERT(pimpl_->session_.get());
228         return *pimpl_->session_.get();
229 }
230
231
232 LyXFunc & LyX::lyxFunc()
233 {
234         return pimpl_->lyxfunc_;
235 }
236
237
238 LyXFunc const & LyX::lyxFunc() const
239 {
240         return pimpl_->lyxfunc_;
241 }
242
243
244 LyXServer & LyX::server()
245 {
246         BOOST_ASSERT(pimpl_->lyx_server_.get());
247         return *pimpl_->lyx_server_.get(); 
248 }
249
250
251 LyXServer const & LyX::server() const 
252 {
253         BOOST_ASSERT(pimpl_->lyx_server_.get());
254         return *pimpl_->lyx_server_.get(); 
255 }
256
257
258 LyXServerSocket & LyX::socket()
259 {
260         BOOST_ASSERT(pimpl_->lyx_socket_.get());
261         return *pimpl_->lyx_socket_.get();
262 }
263
264
265 LyXServerSocket const & LyX::socket() const
266 {
267         BOOST_ASSERT(pimpl_->lyx_socket_.get());
268         return *pimpl_->lyx_socket_.get();
269 }
270
271
272 frontend::Application & LyX::application()
273 {
274         BOOST_ASSERT(pimpl_->application_.get());
275         return *pimpl_->application_.get();
276 }
277
278
279 frontend::Application const & LyX::application() const
280 {
281         BOOST_ASSERT(pimpl_->application_.get());
282         return *pimpl_->application_.get();
283 }
284
285
286 kb_keymap & LyX::topLevelKeymap()
287 {
288         BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
289         return *pimpl_->toplevel_keymap_.get();
290 }
291
292
293 IconvProcessor & LyX::iconvProcessor()
294 {
295         return pimpl_->iconv;
296 }
297
298
299 kb_keymap const & LyX::topLevelKeymap() const
300 {
301         BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
302         return *pimpl_->toplevel_keymap_.get();
303 }
304
305
306 Buffer const * const LyX::updateInset(InsetBase const * inset) const
307 {
308         if (!inset)
309                 return 0;
310
311         Buffer const * buffer_ptr = 0;
312         vector<int> const & view_ids = pimpl_->application_->gui().viewIds();
313         vector<int>::const_iterator it = view_ids.begin();
314         vector<int>::const_iterator const end = view_ids.end();
315         for (; it != end; ++it) {
316                 Buffer const * ptr =
317                         pimpl_->application_->gui().view(*it).updateInset(inset);
318                 if (ptr)
319                         buffer_ptr = ptr;
320         }
321         return buffer_ptr;
322 }
323
324
325 int LyX::exec(int & argc, char * argv[])
326 {
327         // Here we need to parse the command line. At least
328         // we need to parse for "-dbg" and "-help"
329         easyParse(argc, argv);
330
331         support::init_package(argv[0], cl_system_support, cl_user_support,
332                                    support::top_build_dir_is_one_level_up);
333
334         vector<string> files;
335         
336         if (!use_gui) {
337                 // FIXME: create a ConsoleApplication
338                 int exit_status = loadFiles(argc, argv, files);
339                 if (exit_status) {
340                         prepareExit();
341                         return exit_status;
342                 }
343
344                 if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
345                         prepareExit();
346                         return EXIT_SUCCESS;
347                 }
348
349                 BufferList::iterator begin = pimpl_->buffer_list_.begin();
350                 BufferList::iterator end = pimpl_->buffer_list_.end();
351
352                 bool final_success = false;
353                 for (BufferList::iterator I = begin; I != end; ++I) {
354                         Buffer * buf = *I;
355                         bool success = false;
356                         buf->dispatch(batch_command, &success);
357                         final_success |= success;                       
358                 }
359                 prepareExit();
360                 return !final_success;
361         }
362
363         // Force adding of font path _before_ Application is initialized
364         support::addFontResources();
365
366         // Let the frontend parse and remove all arguments that it knows
367         pimpl_->application_.reset(createApplication(argc, argv));
368         // FIXME: this global pointer should probably go.
369         theApp = pimpl_->application_.get();
370
371         initGuiFont();
372
373         // Parse and remove all known arguments in the LyX singleton
374         // Give an error for all remaining ones.
375         int exit_status = loadFiles(argc, argv, files);
376         if (exit_status) {
377                 // Kill the application object before exiting.
378                 pimpl_->application_.reset();
379                 use_gui = false;
380                 prepareExit();
381                 return exit_status;
382         }
383
384         restoreGuiSession(files);
385         // Start the real execution loop.
386
387         // FIXME
388         /* Create a CoreApplication class that will provide the main event loop
389         * and the socket callback registering. With Qt4, only QtCore
390         * library would be needed.
391         * When this is done, a server_mode could be created and the following two
392         * line would be moved out from here.
393         */
394         pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
395         pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_, 
396                 support::os::internal_path(package().temp_dir() + "/lyxsocket")));
397
398         exit_status = pimpl_->application_->exec();
399         // Kill the application object before exiting. This avoid crash
400         // on exit on Linux.
401         pimpl_->application_.reset();
402         // Restore original font resources after Application is destroyed.
403         support::restoreFontResources();
404
405         return exit_status;
406 }
407
408
409 void LyX::prepareExit()
410 {
411         // Set a flag that we do quitting from the program,
412         // so no refreshes are necessary.
413         quitting = true;
414
415         // close buffers first
416         pimpl_->buffer_list_.closeAll();
417
418         // do any other cleanup procedures now
419         lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
420
421         // Prevent the deletion of /tmp if LyX was called with invalid
422         // arguments. Does not work on windows.
423         // FIXME: Fix the real bug instead.
424         if (package().temp_dir() == "/tmp") {
425                 lyxerr << "Not deleting /tmp." << endl;
426                 return;
427         }
428
429         if (!destroyDir(package().temp_dir())) {
430                 docstring const msg =
431                         bformat(_("Unable to remove the temporary directory %1$s"),
432                         from_utf8(package().temp_dir()));
433                 Alert::warning(_("Unable to remove temporary directory"), msg);
434         }
435 }
436
437
438 void LyX::earlyExit(int status)
439 {
440         BOOST_ASSERT(pimpl_->application_.get());
441         // LyX::pimpl_::application_ is not initialised at this
442         // point so it's safe to just exit after some cleanup.
443         prepareExit();
444         exit(status);
445 }
446
447
448 void LyX::quit()
449 {
450         lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
451
452         prepareExit();
453         if (use_gui) {
454                 if (pimpl_->session_)
455                         pimpl_->session_->writeFile();
456                 pimpl_->lyx_server_.reset();
457                 pimpl_->lyx_socket_.reset();
458                 if (pimpl_->application_)
459                         pimpl_->application_->exit(0);
460                 theApp = 0;
461         }
462 }
463
464
465 int LyX::loadFiles(int & argc, char * argv[],
466         vector<string> & files)
467 {
468         // check for any spurious extra arguments
469         // other than documents
470         for (int argi = 1; argi < argc ; ++argi) {
471                 if (argv[argi][0] == '-') {
472                         lyxerr << to_utf8(
473                                 bformat(_("Wrong command line option `%1$s'. Exiting."),
474                                 from_utf8(argv[argi]))) << endl;
475                         return EXIT_FAILURE;
476                 }
477         }
478
479         // Initialization of LyX (reads lyxrc and more)
480         lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
481         bool success = init();
482         lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
483         if (!success)
484                 return EXIT_FAILURE;
485
486         for (int argi = argc - 1; argi >= 1; --argi) {
487                 // check for any remaining extra arguments other than
488                 // document file names. These will be passed out to the
489                 // frontend.
490                 if (argv[argi][0] == '-')
491                         continue;
492                 files.push_back(os::internal_path(argv[argi]));
493         }
494
495         if (first_start)
496                 files.push_back(i18nLibFileSearch("examples", "splash.lyx").absFilename());
497
498         Buffer * last_loaded = 0;
499
500         vector<string>::const_iterator it = files.begin();
501         vector<string>::const_iterator end = files.end();
502
503         for (; it != end; ++it) {
504                 // get absolute path of file and add ".lyx" to
505                 // the filename if necessary
506                 string s = fileSearch(string(), *it, "lyx").absFilename();
507                 if (s.empty()) {
508                         Buffer * const b = newFile(*it, string(), true);
509                         if (b)
510                                 last_loaded = b;
511                 } else {
512                         Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
513                         if (loadLyXFile(buf, s)) {
514                                 last_loaded = buf;
515                                 ErrorList const & el = buf->errorList("Parse");
516                                 if (!el.empty())
517                                         for_each(el.begin(), el.end(),
518                                         boost::bind(&LyX::printError, this, _1));
519                         }
520                         else
521                                 pimpl_->buffer_list_.release(buf);
522                 }
523         }
524
525         files.clear(); // the files are already loaded
526
527         return EXIT_SUCCESS;
528 }
529
530
531 void LyX::execBatchCommands()
532 {
533         // Execute batch commands if available
534         if (batch_command.empty())
535                 return;
536
537         lyxerr[Debug::INIT] << "About to handle -x '"
538                 << batch_command << '\'' << endl;
539
540         pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
541 }
542
543
544 void LyX::restoreGuiSession(vector<string> const & files)
545 {
546         LyXView * view = newLyXView();
547
548         // load files
549         for_each(files.begin(), files.end(),
550                 bind(&LyXView::loadLyXFile, view, _1, true));
551
552         // if a file is specified, I assume that user wants to edit *that* file
553         if (files.empty() && lyxrc.load_session) {
554                 vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
555                 // do not add to the lastfile list since these files are restored from
556                 // last seesion, and should be already there (regular files), or should
557                 // not be added at all (help files).
558                 for_each(lastopened.begin(), lastopened.end(),
559                         bind(&LyXView::loadLyXFile, view, _1, false));
560         }
561         // clear this list to save a few bytes of RAM
562         pimpl_->session_->lastOpened().clear();
563 }
564
565
566 LyXView * LyX::newLyXView()
567 {
568         if (!lyx::use_gui)
569                 return 0;
570
571         // determine windows size and position, from lyxrc and/or session
572         // initial geometry
573         unsigned int width = 690;
574         unsigned int height = 510;
575         // default icon size, will be overwritten by  stored session value
576         unsigned int iconSizeXY = 0;
577         bool maximize = false;
578         // first try lyxrc
579         if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
580                 width = lyxrc.geometry_width;
581                 height = lyxrc.geometry_height;
582         }
583         // if lyxrc returns (0,0), then use session info
584         else {
585                 string val = session().sessionInfo().load("WindowWidth");
586                 if (!val.empty())
587                         width = convert<unsigned int>(val);
588                 val = session().sessionInfo().load("WindowHeight");
589                 if (!val.empty())
590                         height = convert<unsigned int>(val);
591                 if (session().sessionInfo().load("WindowIsMaximized") == "yes")
592                         maximize = true;
593                 val = session().sessionInfo().load("IconSizeXY");
594                 if (!val.empty())
595                         iconSizeXY = convert<unsigned int>(val);
596         }
597
598         // if user wants to restore window position
599         int posx = -1;
600         int posy = -1;
601         if (lyxrc.geometry_xysaved) {
602                 string val = session().sessionInfo().load("WindowPosX");
603                 if (!val.empty())
604                         posx = convert<int>(val);
605                 val = session().sessionInfo().load("WindowPosY");
606                 if (!val.empty())
607                         posy = convert<int>(val);
608         }
609
610         if (!geometryArg.empty()) 
611         {
612                 width = 0;
613                 height = 0;
614         }
615
616         // create the main window
617         LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
618
619         return view;
620 }
621
622 /*
623 Signals and Windows
624 ===================
625 The SIGHUP signal does not exist on Windows and does not need to be handled.
626
627 Windows handles SIGFPE and SIGSEGV signals as expected.
628
629 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
630 cause a new thread to be spawned. This may well result in unexpected
631 behaviour by the single-threaded LyX.
632
633 SIGTERM signals will come only from another process actually sending
634 that signal using 'raise' in Windows' POSIX compatability layer. It will
635 not come from the general "terminate process" methods that everyone
636 actually uses (and which can't be trapped). Killing an app 'politely' on
637 Windows involves first sending a WM_CLOSE message, something that is
638 caught already by the Qt frontend.
639
640 For more information see:
641
642 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
643 ...signals are mostly useless on Windows for a variety of reasons that are
644 Windows specific...
645
646 'UNIX Application Migration Guide, Chapter 9'
647 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
648
649 'How To Terminate an Application "Cleanly" in Win32'
650 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
651 */
652 extern "C" {
653
654 static void error_handler(int err_sig)
655 {
656         // Throw away any signals other than the first one received.
657         static sig_atomic_t handling_error = false;
658         if (handling_error)
659                 return;
660         handling_error = true;
661
662         // We have received a signal indicating a fatal error, so
663         // try and save the data ASAP.
664         LyX::cref().emergencyCleanup();
665
666         // These lyxerr calls may or may not work:
667
668         // Signals are asynchronous, so the main program may be in a very
669         // fragile state when a signal is processed and thus while a signal
670         // handler function executes.
671         // In general, therefore, we should avoid performing any
672         // I/O operations or calling most library and system functions from
673         // signal handlers.
674
675         // This shouldn't matter here, however, as we've already invoked
676         // emergencyCleanup.
677         switch (err_sig) {
678 #ifdef SIGHUP
679         case SIGHUP:
680                 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
681                 break;
682 #endif
683         case SIGFPE:
684                 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
685                 break;
686         case SIGSEGV:
687                 lyxerr << "\nlyx: SIGSEGV signal caught\n"
688                           "Sorry, you have found a bug in LyX. "
689                           "Please read the bug-reporting instructions "
690                           "in Help->Introduction and send us a bug report, "
691                           "if necessary. Thanks !\nBye." << endl;
692                 break;
693         case SIGINT:
694         case SIGTERM:
695                 // no comments
696                 break;
697         }
698
699         // Deinstall the signal handlers
700 #ifdef SIGHUP
701         signal(SIGHUP, SIG_DFL);
702 #endif
703         signal(SIGINT, SIG_DFL);
704         signal(SIGFPE, SIG_DFL);
705         signal(SIGSEGV, SIG_DFL);
706         signal(SIGTERM, SIG_DFL);
707
708 #ifdef SIGHUP
709         if (err_sig == SIGSEGV ||
710             (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
711 #else
712         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
713 #endif
714                 support::abort();
715         exit(0);
716 }
717
718 }
719
720
721 void LyX::printError(ErrorItem const & ei)
722 {
723         docstring tmp = _("LyX: ") + ei.error + char_type(':')
724                 + ei.description;
725         std::cerr << to_utf8(tmp) << std::endl;
726 }
727
728
729 void LyX::initGuiFont()
730 {
731         if (lyxrc.roman_font_name.empty())
732                 lyxrc.roman_font_name = pimpl_->application_->romanFontName();
733
734         if (lyxrc.sans_font_name.empty())
735                 lyxrc.sans_font_name = pimpl_->application_->sansFontName();
736
737         if (lyxrc.typewriter_font_name.empty())
738                 lyxrc.typewriter_font_name 
739                         = pimpl_->application_->typewriterFontName();
740 }
741
742
743 bool LyX::init()
744 {
745 #ifdef SIGHUP
746         signal(SIGHUP, error_handler);
747 #endif
748         signal(SIGFPE, error_handler);
749         signal(SIGSEGV, error_handler);
750         signal(SIGINT, error_handler);
751         signal(SIGTERM, error_handler);
752         // SIGPIPE can be safely ignored.
753
754         lyxrc.tempdir_path = package().temp_dir();
755         lyxrc.document_path = package().document_dir();
756
757         if (lyxrc.template_path.empty()) {
758                 lyxrc.template_path = addPath(package().system_support(),
759                                               "templates");
760         }
761
762         //
763         // Read configuration files
764         //
765
766         // This one may have been distributed along with LyX.
767         if (!readRcFile("lyxrc.dist"))
768                 return false;
769
770         // Set the PATH correctly.
771 #if !defined (USE_POSIX_PACKAGING)
772         // Add the directory containing the LyX executable to the path
773         // so that LyX can find things like tex2lyx.
774         if (package().build_support().empty())
775                 prependEnvPath("PATH", package().binary_dir());
776 #endif
777         if (!lyxrc.path_prefix.empty())
778                 prependEnvPath("PATH", lyxrc.path_prefix);
779
780         // Check that user LyX directory is ok.
781         if (queryUserLyXDir(package().explicit_user_support()))
782                 reconfigureUserLyXDir();
783
784         // no need for a splash when there is no GUI
785         if (!use_gui) {
786                 first_start = false;
787         }
788
789         // This one is generated in user_support directory by lib/configure.py.
790         if (!readRcFile("lyxrc.defaults"))
791                 return false;
792
793         // Query the OS to know what formats are viewed natively
794         formats.setAutoOpen();
795
796         // Read lyxrc.dist again to be able to override viewer auto-detection.
797         readRcFile("lyxrc.dist");
798
799         system_lyxrc = lyxrc;
800         system_formats = formats;
801         system_converters = converters;
802         system_movers = movers;
803         system_lcolor = lcolor;
804
805         // This one is edited through the preferences dialog.
806         if (!readRcFile("preferences"))
807                 return false;
808
809         if (!readEncodingsFile("encodings"))
810                 return false;
811         if (!readLanguagesFile("languages"))
812                 return false;
813
814         // Load the layouts
815         lyxerr[Debug::INIT] << "Reading layouts..." << endl;
816         if (!LyXSetStyle())
817                 return false;
818
819         if (use_gui) {
820                 // Set up bindings
821                 pimpl_->toplevel_keymap_.reset(new kb_keymap);
822                 defaultKeyBindings(pimpl_->toplevel_keymap_.get());
823                 pimpl_->toplevel_keymap_->read(lyxrc.bind_file);
824
825                 pimpl_->lyxfunc_.initKeySequences(pimpl_->toplevel_keymap_.get());
826
827                 // Read menus
828                 if (!readUIFile(lyxrc.ui_file))
829                         return false;
830         }
831
832         if (lyxerr.debugging(Debug::LYXRC))
833                 lyxrc.print();
834
835         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
836         if (!lyxrc.path_prefix.empty())
837                 prependEnvPath("PATH", lyxrc.path_prefix);
838
839         if (fs::exists(lyxrc.document_path) &&
840             fs::is_directory(lyxrc.document_path))
841                 package().document_dir() = lyxrc.document_path;
842
843         package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
844         if (package().temp_dir().empty()) {
845                 Alert::error(_("Could not create temporary directory"),
846                              bformat(_("Could not create a temporary directory in\n"
847                                                     "%1$s. Make sure that this\n"
848                                                     "path exists and is writable and try again."),
849                                      from_utf8(lyxrc.tempdir_path)));
850                 // createLyXTmpDir() tries sufficiently hard to create a
851                 // usable temp dir, so the probability to come here is
852                 // close to zero. We therefore don't try to overcome this
853                 // problem with e.g. asking the user for a new path and
854                 // trying again but simply exit.
855                 return false;
856         }
857
858         if (lyxerr.debugging(Debug::INIT)) {
859                 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
860         }
861
862         lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
863         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
864
865         // This must happen after package initialization and after lyxrc is
866         // read, therefore it can't be done by a static object.
867         ConverterCache::init();
868
869         return true;
870 }
871
872
873 void LyX::defaultKeyBindings(kb_keymap  * kbmap)
874 {
875         kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
876         kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
877         kbmap->bind("Up", FuncRequest(LFUN_UP));
878         kbmap->bind("Down", FuncRequest(LFUN_DOWN));
879
880         kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
881         kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
882         kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
883         kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
884
885         kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
886         kbmap->bind("End", FuncRequest(LFUN_LINE_END));
887         kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
888         kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
889
890         kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
891         //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
892
893         kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
894         kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
895
896         // kbmap->bindings to enable the use of the numeric keypad
897         // e.g. Num Lock set
898         //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
899         //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
900         kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
901         //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
902         //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
903         //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
904         //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
905         //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
906         //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
907         //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
908         //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
909         //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
910         //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
911         //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
912         //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
913         //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
914         kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
915         kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
916         kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
917         kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
918         kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
919         kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
920         kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
921         kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
922 }
923
924
925 void LyX::emergencyCleanup() const
926 {
927         // what to do about tmpfiles is non-obvious. we would
928         // like to delete any we find, but our lyxdir might
929         // contain documents etc. which might be helpful on
930         // a crash
931
932         pimpl_->buffer_list_.emergencyWriteAll();
933         if (use_gui) {
934                 if (pimpl_->lyx_server_)
935                         pimpl_->lyx_server_->emergencyCleanup();
936                 pimpl_->lyx_server_.reset();
937                 pimpl_->lyx_socket_.reset();
938         }
939 }
940
941
942 void LyX::deadKeyBindings(kb_keymap * kbmap)
943 {
944         // bindKeyings for transparent handling of deadkeys
945         // The keysyms are gotten from XFree86 X11R6
946         kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
947         kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
948         kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
949         kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
950         kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
951         kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
952         kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
953         kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
954         kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
955         kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
956         // nothing with this name
957         // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
958         kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
959         kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
960         // nothing with this name either...
961         //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
962         kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
963         kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
964         kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
965 }
966
967
968 namespace {
969
970 // return true if file does not exist or is older than configure.py.
971 bool needsUpdate(string const & file)
972 {
973         static string const configure_script =
974                 addName(package().system_support(), "configure.py");
975         string const absfile =
976                 addName(package().user_support(), file);
977
978         return (! fs::exists(absfile))
979                 || (fs::last_write_time(configure_script)
980                     > fs::last_write_time(absfile));
981 }
982
983 }
984
985
986 bool LyX::queryUserLyXDir(bool explicit_userdir)
987 {
988         // Does user directory exist?
989         if (fs::exists(package().user_support()) &&
990             fs::is_directory(package().user_support())) {
991                 first_start = false;
992
993                 return needsUpdate("lyxrc.defaults")
994                         || needsUpdate("textclass.lst")
995                         || needsUpdate("packages.lst");
996         }
997
998         first_start = !explicit_userdir;
999
1000         // If the user specified explicitly a directory, ask whether
1001         // to create it. If the user says "no", then exit.
1002         if (explicit_userdir &&
1003             Alert::prompt(
1004                     _("Missing user LyX directory"),
1005                     bformat(_("You have specified a non-existent user "
1006                                            "LyX directory, %1$s.\n"
1007                                            "It is needed to keep your own configuration."),
1008                             from_utf8(package().user_support())),
1009                     1, 0,
1010                     _("&Create directory"),
1011                     _("&Exit LyX"))) {
1012                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1013                 earlyExit(EXIT_FAILURE);
1014         }
1015
1016         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1017                           from_utf8(package().user_support())))
1018                << endl;
1019
1020         if (!createDirectory(package().user_support(), 0755)) {
1021                 // Failed, so let's exit.
1022                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1023                        << endl;
1024                 earlyExit(EXIT_FAILURE);
1025         }
1026
1027         return true;
1028 }
1029
1030
1031 bool LyX::readRcFile(string const & name)
1032 {
1033         lyxerr[Debug::INIT] << "About to read " << name << "... ";
1034
1035         FileName const lyxrc_path = libFileSearch(string(), name);
1036         if (!lyxrc_path.empty()) {
1037
1038                 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
1039
1040                 if (lyxrc.read(lyxrc_path) < 0) {
1041                         showFileError(name);
1042                         return false;
1043                 }
1044         } else
1045                 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
1046         return true;
1047
1048 }
1049
1050
1051 // Read the ui file `name'
1052 bool LyX::readUIFile(string const & name)
1053 {
1054         enum Uitags {
1055                 ui_menuset = 1,
1056                 ui_toolbar,
1057                 ui_toolbars,
1058                 ui_include,
1059                 ui_last
1060         };
1061
1062         struct keyword_item uitags[ui_last - 1] = {
1063                 { "include", ui_include },
1064                 { "menuset", ui_menuset },
1065                 { "toolbar", ui_toolbar },
1066                 { "toolbars", ui_toolbars }
1067         };
1068
1069         // Ensure that a file is read only once (prevents include loops)
1070         static std::list<string> uifiles;
1071         std::list<string>::const_iterator it  = uifiles.begin();
1072         std::list<string>::const_iterator end = uifiles.end();
1073         it = std::find(it, end, name);
1074         if (it != end) {
1075                 lyxerr[Debug::INIT] << "UI file '" << name
1076                                     << "' has been read already. "
1077                                     << "Is this an include loop?"
1078                                     << endl;
1079                 return false;
1080         }
1081
1082         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1083
1084         FileName const ui_path = libFileSearch("ui", name, "ui");
1085
1086         if (ui_path.empty()) {
1087                 lyxerr[Debug::INIT] << "Could not find " << name << endl;
1088                 showFileError(name);
1089                 return false;
1090         }
1091         uifiles.push_back(name);
1092
1093         lyxerr[Debug::INIT] << "Found " << name
1094                             << " in " << ui_path << endl;
1095         LyXLex lex(uitags, ui_last - 1);
1096         lex.setFile(ui_path);
1097         if (!lex.isOK()) {
1098                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1099                        << endl;
1100         }
1101
1102         if (lyxerr.debugging(Debug::PARSER))
1103                 lex.printTable(lyxerr);
1104
1105         while (lex.isOK()) {
1106                 switch (lex.lex()) {
1107                 case ui_include: {
1108                         lex.next(true);
1109                         string const file = lex.getString();
1110                         if (!readUIFile(file))
1111                                 return false;
1112                         break;
1113                 }
1114                 case ui_menuset:
1115                         menubackend.read(lex);
1116                         break;
1117
1118                 case ui_toolbar:
1119                         toolbarbackend.read(lex);
1120                         break;
1121
1122                 case ui_toolbars:
1123                         toolbarbackend.readToolbars(lex);
1124                         break;
1125
1126                 default:
1127                         if (!rtrim(lex.getString()).empty())
1128                                 lex.printError("LyX::ReadUIFile: "
1129                                                "Unknown menu tag: `$$Token'");
1130                         break;
1131                 }
1132         }
1133         return true;
1134 }
1135
1136
1137 // Read the languages file `name'
1138 bool LyX::readLanguagesFile(string const & name)
1139 {
1140         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1141
1142         FileName const lang_path = libFileSearch(string(), name);
1143         if (lang_path.empty()) {
1144                 showFileError(name);
1145                 return false;
1146         }
1147         languages.read(lang_path);
1148         return true;
1149 }
1150
1151
1152 // Read the encodings file `name'
1153 bool LyX::readEncodingsFile(string const & name)
1154 {
1155         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
1156
1157         FileName const enc_path = libFileSearch(string(), name);
1158         if (enc_path.empty()) {
1159                 showFileError(name);
1160                 return false;
1161         }
1162         encodings.read(enc_path);
1163         return true;
1164 }
1165
1166
1167 namespace {
1168
1169 string batch;
1170
1171 /// return the the number of arguments consumed
1172 typedef boost::function<int(string const &, string const &)> cmd_helper;
1173
1174 int parse_dbg(string const & arg, string const &)
1175 {
1176         if (arg.empty()) {
1177                 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
1178                 Debug::showTags(lyxerr);
1179                 exit(0);
1180         }
1181         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1182
1183         lyxerr.level(Debug::value(arg));
1184         Debug::showLevel(lyxerr, lyxerr.level());
1185         return 1;
1186 }
1187
1188
1189 int parse_help(string const &, string const &)
1190 {
1191         lyxerr <<
1192                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1193                   "Command line switches (case sensitive):\n"
1194                   "\t-help              summarize LyX usage\n"
1195                   "\t-userdir dir       set user directory to dir\n"
1196                   "\t-sysdir dir        set system directory to dir\n"
1197                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1198                   "\t-dbg feature[,feature]...\n"
1199                   "                  select the features to debug.\n"
1200                   "                  Type `lyx -dbg' to see the list of features\n"
1201                   "\t-x [--execute] command\n"
1202                   "                  where command is a lyx command.\n"
1203                   "\t-e [--export] fmt\n"
1204                   "                  where fmt is the export format of choice.\n"
1205                   "\t-i [--import] fmt file.xxx\n"
1206                   "                  where fmt is the import format of choice\n"
1207                   "                  and file.xxx is the file to be imported.\n"
1208                   "\t-version        summarize version and build info\n"
1209                                "Check the LyX man page for more details.")) << endl;
1210         exit(0);
1211         return 0;
1212 }
1213
1214 int parse_version(string const &, string const &)
1215 {
1216         lyxerr << "LyX " << lyx_version
1217                << " (" << lyx_release_date << ")" << endl;
1218         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1219
1220         lyxerr << lyx_version_info << endl;
1221         exit(0);
1222         return 0;
1223 }
1224
1225 int parse_sysdir(string const & arg, string const &)
1226 {
1227         if (arg.empty()) {
1228                 lyxerr << to_utf8(_("Missing directory for -sysdir switch")) << endl;
1229                 exit(1);
1230         }
1231         cl_system_support = arg;
1232         return 1;
1233 }
1234
1235 int parse_userdir(string const & arg, string const &)
1236 {
1237         if (arg.empty()) {
1238                 lyxerr << to_utf8(_("Missing directory for -userdir switch")) << endl;
1239                 exit(1);
1240         }
1241         cl_user_support = arg;
1242         return 1;
1243 }
1244
1245 int parse_execute(string const & arg, string const &)
1246 {
1247         if (arg.empty()) {
1248                 lyxerr << to_utf8(_("Missing command string after --execute switch")) << endl;
1249                 exit(1);
1250         }
1251         batch = arg;
1252         return 1;
1253 }
1254
1255 int parse_export(string const & type, string const &)
1256 {
1257         if (type.empty()) {
1258                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1259                                          "--export switch")) << endl;
1260                 exit(1);
1261         }
1262         batch = "buffer-export " + type;
1263         use_gui = false;
1264         return 1;
1265 }
1266
1267 int parse_import(string const & type, string const & file)
1268 {
1269         if (type.empty()) {
1270                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1271                                          "--import switch")) << endl;
1272                 exit(1);
1273         }
1274         if (file.empty()) {
1275                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1276                 exit(1);
1277         }
1278
1279         batch = "buffer-import " + type + ' ' + file;
1280         return 2;
1281 }
1282
1283 int parse_geometry(string const & arg1, string const &)
1284 {
1285         geometryArg = arg1;
1286 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
1287         // remove also the arg
1288         return 1;
1289 #else
1290         // don't remove "-geometry"
1291         return -1;
1292 #endif
1293 }
1294
1295
1296 } // namespace anon
1297
1298
1299 void LyX::easyParse(int & argc, char * argv[])
1300 {
1301         std::map<string, cmd_helper> cmdmap;
1302
1303         cmdmap["-dbg"] = parse_dbg;
1304         cmdmap["-help"] = parse_help;
1305         cmdmap["--help"] = parse_help;
1306         cmdmap["-version"] = parse_version;
1307         cmdmap["--version"] = parse_version;
1308         cmdmap["-sysdir"] = parse_sysdir;
1309         cmdmap["-userdir"] = parse_userdir;
1310         cmdmap["-x"] = parse_execute;
1311         cmdmap["--execute"] = parse_execute;
1312         cmdmap["-e"] = parse_export;
1313         cmdmap["--export"] = parse_export;
1314         cmdmap["-i"] = parse_import;
1315         cmdmap["--import"] = parse_import;
1316         cmdmap["-geometry"] = parse_geometry;
1317
1318         for (int i = 1; i < argc; ++i) {
1319                 std::map<string, cmd_helper>::const_iterator it
1320                         = cmdmap.find(argv[i]);
1321
1322                 // don't complain if not found - may be parsed later
1323                 if (it == cmdmap.end())
1324                         continue;
1325
1326                 string arg((i + 1 < argc) ? argv[i + 1] : "");
1327                 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1328
1329                 int const remove = 1 + it->second(arg, arg2);
1330
1331                 // Now, remove used arguments by shifting
1332                 // the following ones remove places down.
1333                 if (remove > 0) {
1334                         argc -= remove;
1335                         for (int j = i; j < argc; ++j)
1336                                 argv[j] = argv[j + remove];
1337                         --i;
1338                 }
1339         }
1340
1341         batch_command = batch;
1342 }
1343
1344
1345 FuncStatus getStatus(FuncRequest const & action)
1346 {
1347         return LyX::ref().lyxFunc().getStatus(action);
1348 }
1349
1350
1351 void dispatch(FuncRequest const & action)
1352 {
1353         LyX::ref().lyxFunc().dispatch(action);
1354 }
1355
1356
1357 BufferList & theBufferList()
1358 {
1359         return LyX::ref().bufferList();
1360 }
1361
1362
1363 LyXFunc & theLyXFunc()
1364 {
1365         return LyX::ref().lyxFunc();
1366 }
1367
1368
1369 LyXServer & theLyXServer()
1370 {
1371         // FIXME: this should not be use_gui dependent
1372         BOOST_ASSERT(use_gui);
1373         return LyX::ref().server();
1374 }
1375
1376
1377 LyXServerSocket & theLyXServerSocket()
1378 {
1379         // FIXME: this should not be use_gui dependent
1380         BOOST_ASSERT(use_gui);
1381         return LyX::ref().socket();
1382 }
1383
1384
1385 kb_keymap & theTopLevelKeymap()
1386 {
1387         BOOST_ASSERT(use_gui);
1388         return LyX::ref().topLevelKeymap();
1389 }
1390
1391
1392 IconvProcessor & utf8ToUcs4()
1393 {
1394         return LyX::ref().iconvProcessor();
1395 }
1396
1397 } // namespace lyx