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