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