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