]> git.lyx.org Git - lyx.git/blob - src/buffer.C
65819731e677eb0035789b7786fec884c017b685
[lyx.git] / src / buffer.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  *           This file is Copyright 1996-2001
10  *           Lars Gullik Bjønnes
11  *
12  * ======================================================
13  */
14
15 #include <config.h>
16
17 #include "buffer.h"
18 #include "bufferlist.h"
19 #include "LyXAction.h"
20 #include "lyxrc.h"
21 #include "lyxlex.h"
22 #include "tex-strings.h"
23 #include "layout.h"
24 #include "bufferview_funcs.h"
25 #include "lyxfont.h"
26 #include "version.h"
27 #include "LaTeX.h"
28 #include "Chktex.h"
29 #include "debug.h"
30 #include "LaTeXFeatures.h"
31 #include "lyxtext.h"
32 #include "gettext.h"
33 #include "language.h"
34 #include "exporter.h"
35 #include "Lsstream.h"
36 #include "format.h"
37 #include "BufferView.h"
38 #include "ParagraphParameters.h"
39 #include "iterators.h"
40 #include "lyxtextclasslist.h"
41 #include "sgml.h"
42 #include "paragraph_funcs.h"
43 #include "author.h"
44
45 #include "frontends/LyXView.h"
46
47 #include "mathed/formulamacro.h"
48 #include "mathed/formula.h"
49
50 #include "insets/inset.h"
51 #include "insets/inseterror.h"
52 #include "insets/insethfill.h"
53 #include "insets/insetlabel.h"
54 #include "insets/insetref.h"
55 #include "insets/inseturl.h"
56 #include "insets/insetnote.h"
57 #include "insets/insetquotes.h"
58 #include "insets/insetlatexaccent.h"
59 #include "insets/insetbibitem.h"
60 #include "insets/insetbibtex.h"
61 #include "insets/insetcite.h"
62 #include "insets/insetexternal.h"
63 #include "insets/insetindex.h"
64 #include "insets/insetinclude.h"
65 #include "insets/insettoc.h"
66 #include "insets/insetparent.h"
67 #include "insets/insetspecialchar.h"
68 #include "insets/insettext.h"
69 #include "insets/insetert.h"
70 #include "insets/insetgraphics.h"
71 #include "insets/insetfoot.h"
72 #include "insets/insetmarginal.h"
73 #include "insets/insetoptarg.h"
74 #include "insets/insetminipage.h"
75 #include "insets/insetfloat.h"
76 #include "insets/insetwrap.h"
77 #include "insets/insettabular.h"
78 #if 0
79 #include "insets/insettheorem.h"
80 #include "insets/insetlist.h"
81 #endif
82 #include "insets/insetcaption.h"
83 #include "insets/insetfloatlist.h"
84
85 #include "frontends/Dialogs.h"
86 #include "frontends/Alert.h"
87
88 #include "graphics/Previews.h"
89
90 #include "support/textutils.h"
91 #include "support/filetools.h"
92 #include "support/path.h"
93 #include "support/os.h"
94 #include "support/lyxlib.h"
95 #include "support/FileInfo.h"
96 #include "support/lyxmanip.h"
97 #include "support/lyxtime.h"
98
99 #include <boost/bind.hpp>
100 #include <boost/tuple/tuple.hpp>
101 #include "BoostFormat.h"
102
103 #include <fstream>
104 #include <iomanip>
105 #include <map>
106 #include <stack>
107 #include <list>
108 #include <algorithm>
109
110 #include <cstdlib>
111 #include <cmath>
112 #include <unistd.h>
113 #include <sys/types.h>
114 #include <utime.h>
115
116 #ifdef HAVE_LOCALE
117 #include <locale>
118 #endif
119
120 #ifndef CXX_GLOBAL_CSTD
121 using std::pow;
122 #endif
123
124 using std::ostream;
125 using std::ofstream;
126 using std::ifstream;
127 using std::fstream;
128 using std::ios;
129 using std::setw;
130 using std::endl;
131 using std::pair;
132 using std::make_pair;
133 using std::vector;
134 using std::map;
135 using std::stack;
136 using std::list;
137 using std::for_each;
138
139 using lyx::pos_type;
140 using lyx::textclass_type;
141
142 // all these externs should eventually be removed.
143 extern BufferList bufferlist;
144
145 namespace {
146
147 const int LYX_FORMAT = 222;
148
149 } // namespace anon
150
151 Buffer::Buffer(string const & file, bool ronly)
152         : niceFile(true), lyx_clean(true), bak_clean(true),
153           unnamed(false), read_only(ronly),
154           filename_(file), users(0)
155 {
156         lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
157         filepath_ = OnlyPath(file);
158         lyxvc.buffer(this);
159         if (read_only || lyxrc.use_tempdir) {
160                 tmppath = CreateBufferTmpDir();
161         } else {
162                 tmppath.erase();
163         }
164
165         // set initial author
166         authors().record(Author(lyxrc.user_name, lyxrc.user_email));
167 }
168
169
170 Buffer::~Buffer()
171 {
172         lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
173         // here the buffer should take care that it is
174         // saved properly, before it goes into the void.
175
176         // make sure that views using this buffer
177         // forgets it.
178         if (users)
179                 users->buffer(0);
180
181         if (!tmppath.empty()) {
182                 DestroyBufferTmpDir(tmppath);
183         }
184
185         paragraphs.clear();
186
187         // Remove any previewed LaTeX snippets assocoated with this buffer.
188         grfx::Previews::get().removeLoader(this);
189 }
190
191
192 string const Buffer::getLatexName(bool no_path) const
193 {
194         string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
195         if (no_path)
196                 return OnlyFilename(name);
197         else
198                 return name;
199 }
200
201
202 pair<Buffer::LogType, string> const Buffer::getLogName() const
203 {
204         string const filename = getLatexName(false);
205
206         if (filename.empty())
207                 return make_pair(Buffer::latexlog, string());
208
209         string path = OnlyPath(filename);
210
211         if (lyxrc.use_tempdir || !IsDirWriteable(path))
212                 path = tmppath;
213
214         string const fname = AddName(path,
215                                      OnlyFilename(ChangeExtension(filename,
216                                                                   ".log")));
217         string const bname =
218                 AddName(path, OnlyFilename(
219                         ChangeExtension(filename,
220                                         formats.extension("literate") + ".out")));
221
222         // If no Latex log or Build log is newer, show Build log
223
224         FileInfo const f_fi(fname);
225         FileInfo const b_fi(bname);
226
227         if (b_fi.exist() &&
228             (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
229                 lyxerr[Debug::FILES] << "Log name calculated as: " << bname << endl;
230                 return make_pair(Buffer::buildlog, bname);
231         }
232         lyxerr[Debug::FILES] << "Log name calculated as: " << fname << endl;
233         return make_pair(Buffer::latexlog, fname);
234 }
235
236
237 void Buffer::setReadonly(bool flag)
238 {
239         if (read_only != flag) {
240                 read_only = flag;
241                 updateTitles();
242                 users->owner()->getDialogs().updateBufferDependent(false);
243         }
244 }
245
246
247 AuthorList & Buffer::authors()
248 {
249         return params.authorlist;
250 }
251
252
253 /// Update window titles of all users
254 // Should work on a list
255 void Buffer::updateTitles() const
256 {
257         if (users)
258                 users->owner()->updateWindowTitle();
259 }
260
261
262 /// Reset autosave timer of all users
263 // Should work on a list
264 void Buffer::resetAutosaveTimers() const
265 {
266         if (users)
267                 users->owner()->resetAutosaveTimer();
268 }
269
270
271 void Buffer::setFileName(string const & newfile)
272 {
273         filename_ = MakeAbsPath(newfile);
274         filepath_ = OnlyPath(filename_);
275         setReadonly(IsFileWriteable(filename_) == 0);
276         updateTitles();
277 }
278
279
280 // We'll remove this later. (Lgb)
281 namespace {
282
283 #ifdef WITH_WARNINGS
284 #warning this is never set to != 0 !!! - remove ?
285 #endif
286 int unknown_layouts;
287
288 void unknownClass(string const & unknown)
289 {
290         string msg =
291 #if USE_BOOST_FORMAT
292                 boost::io::str(boost::format(
293                         _("The document uses an unknown textclass \"%1$s\".")) % unknown)
294                 + _("-- substituting default.");
295 #else
296                 _("The document uses an unknown textclass ")
297                 + unknown + _("-- substituting default.");
298 #endif
299         Alert::alert(_("Textclass error"), msg);
300 }
301
302 } // anon
303
304 int Buffer::readHeader(LyXLex & lex)
305 {
306         int unknown_tokens = 0;
307
308         while (lex.isOK()) {
309                 lex.nextToken();
310                 string const token = lex.getString();
311
312                 if (token.empty())
313                         continue;
314
315                 if (token == "\\end_header")
316                         break;
317
318                 lyxerr[Debug::PARSER] << "Handling header token: `"
319                                       << token << '\'' << endl;
320
321                 string unknown = params.readToken(lex, token);
322                 if (!unknown.empty()) {
323                         if (unknown[0] != '\\') {
324                                 unknownClass(unknown);
325                         } else {
326                         ++unknown_tokens;
327                         }
328                 }
329         }
330         return unknown_tokens;
331 }
332
333
334 // candidate for move to BufferView
335 // (at least some parts in the beginning of the func)
336 //
337 // Uwe C. Schroeder
338 // changed to be public and have one parameter
339 // if par = 0 normal behavior
340 // else insert behavior
341 // Returns false if "\the_end" is not read (Asger)
342 bool Buffer::readBody(LyXLex & lex, ParagraphList::iterator pit)
343 {
344         unknown_layouts = 0;
345         int unknown_tokens = 0;
346
347         int pos = 0;
348         Paragraph::depth_type depth = 0;
349         bool the_end_read = false;
350
351         LyXFont font(LyXFont::ALL_INHERIT, params.language);
352
353         if (paragraphs.empty()) {
354                 unknown_tokens += readHeader(lex);
355
356                 if (!params.getLyXTextClass().load()) {
357 #if USE_BOOST_FORMAT
358                         Alert::alert(_("Textclass Loading Error!"),
359                                    boost::io::str(boost::format(_("Can't load textclass %1$s")) %
360                                    params.getLyXTextClass().name()),
361                                    _("-- substituting default."));
362 #else
363                         Alert::alert(_("Textclass Loading Error!"),
364                                      _("Can't load textclass ")
365                                      + params.getLyXTextClass().name(),
366                                      _("-- substituting default."));
367 #endif
368                         params.textclass = 0;
369                 }
370         } else {
371                 // We are inserting into an existing document
372                 users->text->breakParagraph(users, paragraphs);
373                 pos = 0;
374                 markDirty();
375
376                 // We don't want to adopt the parameters from the
377                 // document we insert, so read them into a temporary buffer
378                 // and then discard it
379
380                 Buffer tmpbuf("", false);
381                 tmpbuf.readHeader(lex);
382         }
383
384         while (lex.isOK()) {
385                 lex.nextToken();
386                 string const token = lex.getString();
387
388                 if (token.empty())
389                         continue;
390
391                 lyxerr[Debug::PARSER] << "Handling token: `"
392                                       << token << '\'' << endl;
393
394                 if (token == "\\the_end") {
395                         the_end_read = true;
396                         continue;
397                 }
398
399                 unknown_tokens += readToken(lex, paragraphs, pit, token, pos, depth, font);
400         }
401
402         if (unknown_layouts > 0) {
403                 string s = _("Couldn't set the layout for ");
404                 if (unknown_layouts == 1) {
405                         s += _("one paragraph");
406                 } else {
407                         s += tostr(unknown_layouts);
408                         s += _(" paragraphs");
409                 }
410 #if USE_BOOST_FORMAT
411                 Alert::alert(_("Textclass Loading Error!"), s,
412                            boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
413 #else
414                 Alert::alert(_("Textclass Loading Error!"), s,
415                              _("When reading ") + fileName());
416 #endif
417         }
418
419         if (unknown_tokens > 0) {
420                 string s = _("Encountered ");
421                 if (unknown_tokens == 1) {
422                         s += _("one unknown token");
423                 } else {
424                         s += tostr(unknown_tokens);
425                         s += _(" unknown tokens");
426                 }
427 #if USE_BOOST_FORMAT
428                 Alert::alert(_("Textclass Loading Error!"), s,
429                            boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
430 #else
431                 Alert::alert(_("Textclass Loading Error!"), s,
432                              _("When reading ") +  fileName());
433 #endif
434         }
435
436         return the_end_read;
437 }
438
439
440 int
441 Buffer::readToken(LyXLex & lex, ParagraphList & pars,
442                   ParagraphList::iterator & pit,
443                   string const & token, int & pos,
444                   Paragraph::depth_type & depth,
445                   LyXFont & font)
446 {
447         static Change current_change;
448         int unknown = 0;
449
450         // The order of the tags tested may seem unnatural, but this
451         // has been done in order to reduce the number of string
452         // comparisons needed to recognize a given token. This leads
453         // on large documents like UserGuide to a reduction of a
454         // factor 5! (JMarc)
455         if (token[0] != '\\') {
456                 for (string::const_iterator cit = token.begin();
457                      cit != token.end(); ++cit) {
458                         pit->insertChar(pos, (*cit), font, current_change);
459                         ++pos;
460                 }
461         } else if (token == "\\layout") {
462                 // reset the font as we start a new layout and if the font is
463                 // not ALL_INHERIT,document_language then it will be set to the
464                 // right values after this tag (Jug 20020420)
465                 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
466
467                 lex.eatLine();
468                 string layoutname = lex.getString();
469
470                 LyXTextClass const & tclass = params.getLyXTextClass();
471
472                 if (layoutname.empty()) {
473                         layoutname = tclass.defaultLayoutName();
474                 }
475                 bool hasLayout = tclass.hasLayout(layoutname);
476                 if (!hasLayout) {
477                         lyxerr << "Layout '" << layoutname << "' does not"
478                                << " exist in textclass '" << tclass.name()
479                                << "'." << endl;
480                         lyxerr << "Trying to use default layout instead."
481                                << endl;
482                         layoutname = tclass.defaultLayoutName();
483                 }
484
485 #ifdef USE_CAPTION
486                 // The is the compability reading of layout caption.
487                 // It can be removed in LyX version 1.3.0. (Lgb)
488                 if (compare_ascii_no_case(layoutname, "caption") == 0) {
489                         // We expect that the par we are now working on is
490                         // really inside a InsetText inside a InsetFloat.
491                         // We also know that captions can only be
492                         // one paragraph. (Lgb)
493
494                         // We should now read until the next "\layout"
495                         // is reached.
496                         // This is probably not good enough, what if the
497                         // caption is the last par in the document (Lgb)
498                         istream & ist = lex.getStream();
499                         stringstream ss;
500                         string line;
501                         int begin = 0;
502                         while (true) {
503                                 getline(ist, line);
504                                 if (prefixIs(line, "\\layout")) {
505                                         lex.pushToken(line);
506                                         break;
507                                 }
508                                 if (prefixIs(line, "\\begin_inset"))
509                                         ++begin;
510                                 if (prefixIs(line, "\\end_inset")) {
511                                         if (begin)
512                                                 --begin;
513                                         else {
514                                                 lex.pushToken(line);
515                                                 break;
516                                         }
517                                 }
518
519                                 ss << line << '\n';
520                         }
521                         // Now we should have the whole layout in ss
522                         // we should now be able to give this to the
523                         // caption inset.
524                         ss << "\\end_inset\n";
525
526                         // This seems like a bug in stringstream.
527                         // We really should be able to use ss
528                         // directly. (Lgb)
529                         istringstream is(ss.str());
530                         LyXLex tmplex(0, 0);
531                         tmplex.setStream(is);
532                         Inset * inset = new InsetCaption;
533                         inset->Read(this, tmplex);
534                         pit->InsertInset(pos, inset, font);
535                         ++pos;
536                 } else {
537 #endif
538                         Paragraph * par = new Paragraph();
539                         if (params.tracking_changes)
540                                 par->trackChanges();
541                         pos = 0;
542                         par->layout(params.getLyXTextClass()[layoutname]);
543                         // Test whether the layout is obsolete.
544                         LyXLayout_ptr const & layout = par->layout();
545                         if (!layout->obsoleted_by().empty())
546                                 par->layout(params.getLyXTextClass()[layout->obsoleted_by()]);
547                         par->params().depth(depth);
548
549                         par->params().read(lex);
550
551                         // insert after
552                         if (pit != pars.end())
553                                 ++pit;
554                         pit = pars.insert(pit, par);
555 #if USE_CAPTION
556                 }
557 #endif
558
559         } else if (token == "\\end_inset") {
560                 lyxerr << "Solitary \\end_inset in line " << lex.getLineNo() << "\n"
561                        << "Missing \\begin_inset?.\n";
562                 // Simply ignore this. The insets do not have
563                 // to read this.
564                 // But insets should read it, it is a part of
565                 // the inset isn't it? Lgb.
566         } else if (token == "\\begin_inset") {
567                 readInset(lex, pit, pos, font, current_change);
568         } else if (token == "\\family") {
569                 lex.next();
570                 font.setLyXFamily(lex.getString());
571         } else if (token == "\\series") {
572                 lex.next();
573                 font.setLyXSeries(lex.getString());
574         } else if (token == "\\shape") {
575                 lex.next();
576                 font.setLyXShape(lex.getString());
577         } else if (token == "\\size") {
578                 lex.next();
579                 font.setLyXSize(lex.getString());
580         } else if (token == "\\lang") {
581                 lex.next();
582                 string const tok = lex.getString();
583                 Language const * lang = languages.getLanguage(tok);
584                 if (lang) {
585                         font.setLanguage(lang);
586                 } else {
587                         font.setLanguage(params.language);
588                         lex.printError("Unknown language `$$Token'");
589                 }
590         } else if (token == "\\numeric") {
591                 lex.next();
592                 font.setNumber(font.setLyXMisc(lex.getString()));
593         } else if (token == "\\emph") {
594                 lex.next();
595                 font.setEmph(font.setLyXMisc(lex.getString()));
596         } else if (token == "\\bar") {
597                 lex.next();
598                 string const tok = lex.getString();
599                 // This is dirty, but gone with LyX3. (Asger)
600                 if (tok == "under")
601                         font.setUnderbar(LyXFont::ON);
602                 else if (tok == "no")
603                         font.setUnderbar(LyXFont::OFF);
604                 else if (tok == "default")
605                         font.setUnderbar(LyXFont::INHERIT);
606                 else
607                         lex.printError("Unknown bar font flag "
608                                        "`$$Token'");
609         } else if (token == "\\noun") {
610                 lex.next();
611                 font.setNoun(font.setLyXMisc(lex.getString()));
612         } else if (token == "\\color") {
613                 lex.next();
614                 font.setLyXColor(lex.getString());
615         } else if (token == "\\SpecialChar") {
616                 LyXLayout_ptr const & layout = pit->layout();
617
618                 // Insets don't make sense in a free-spacing context! ---Kayvan
619                 if (layout->free_spacing || pit->isFreeSpacing()) {
620                         if (lex.isOK()) {
621                                 lex.next();
622                                 string const next_token = lex.getString();
623                                 if (next_token == "\\-") {
624                                         pit->insertChar(pos, '-', font, current_change);
625                                 } else if (next_token == "~") {
626                                         pit->insertChar(pos, ' ', font, current_change);
627                                 } else {
628                                         lex.printError("Token `$$Token' "
629                                                        "is in free space "
630                                                        "paragraph layout!");
631                                         --pos;
632                                 }
633                         }
634                 } else {
635                         Inset * inset = new InsetSpecialChar;
636                         inset->read(this, lex);
637                         pit->insertInset(pos, inset, font, current_change);
638                 }
639                 ++pos;
640         } else if (token == "\\i") {
641                 Inset * inset = new InsetLatexAccent;
642                 inset->read(this, lex);
643                 pit->insertInset(pos, inset, font, current_change);
644                 ++pos;
645         } else if (token == "\\backslash") {
646                 pit->insertChar(pos, '\\', font, current_change);
647                 ++pos;
648         } else if (token == "\\begin_deeper") {
649                 ++depth;
650         } else if (token == "\\end_deeper") {
651                 if (!depth) {
652                         lex.printError("\\end_deeper: "
653                                        "depth is already null");
654                 }
655                 else
656                         --depth;
657                 // do not delete this token, it is still needed!
658         } else if (token == "\\newline") {
659                 pit->insertChar(pos, Paragraph::META_NEWLINE, font, current_change);
660                 ++pos;
661         } else if (token == "\\LyXTable") {
662                 Inset * inset = new InsetTabular(*this);
663                 inset->read(this, lex);
664                 pit->insertInset(pos, inset, font, current_change);
665                 ++pos;
666         } else if (token == "\\bibitem") {  // ale970302
667                 InsetCommandParams p("bibitem", "dummy");
668                 InsetBibitem * inset = new InsetBibitem(p);
669                 inset->read(this, lex);
670                 pit->insertInset(pos, inset, font, current_change);
671                 ++pos;
672         } else if (token == "\\hfill") {
673                 pit->insertInset(pos, new InsetHFill(),
674                         LyXFont(LyXFont::ALL_INHERIT, params.language));
675                 ++pos;
676         } else if (token == "\\change_unchanged") {
677                 // Hack ! Needed for empty paragraphs :/
678                 if (!pos)
679                         pit->cleanChanges();
680                 current_change = Change(Change::UNCHANGED);
681         } else if (token == "\\change_inserted") {
682                 lex.nextToken();
683                 istringstream istr(lex.getString());
684                 int aid;
685                 lyx::time_type ct;
686                 istr >> aid;
687                 istr >> ct;
688                 current_change = Change(Change::INSERTED, params.author_ids[aid], ct);
689         } else if (token == "\\change_deleted") {
690                 lex.nextToken();
691                 istringstream istr(lex.getString());
692                 int aid;
693                 lyx::time_type ct;
694                 istr >> aid;
695                 istr >> ct;
696                 current_change = Change(Change::DELETED, params.author_ids[aid], ct);
697         } else {
698                 // This should be insurance for the future: (Asger)
699                 ++unknown;
700                 lex.eatLine();
701 #if USE_BOOST_FORMAT
702                 boost::format fmt(_("Unknown token: %1$s %2$s\n"));
703                 fmt % token % lex.text();
704                 string const s = fmt.str();
705 #else
706                 string const s = _("Unknown token: ") + token
707                         + ' ' + lex.text() + '\n';
708 #endif
709                 // we can do this here this way because we're actually reading
710                 // the buffer and don't care about LyXText right now.
711                 InsetError * new_inset = new InsetError(s);
712                 pit->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
713                                  params.language));
714
715         }
716
717         return unknown;
718 }
719
720
721 // needed to insert the selection
722 void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
723                                  LyXFont const & fn,string const & str)
724 {
725         LyXLayout_ptr const & layout = par->layout();
726
727         LyXFont font = fn;
728
729         par->checkInsertChar(font);
730         // insert the string, don't insert doublespace
731         bool space_inserted = true;
732         bool autobreakrows = !par->inInset() ||
733                 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
734         for(string::const_iterator cit = str.begin();
735             cit != str.end(); ++cit) {
736                 if (*cit == '\n') {
737                         if (autobreakrows && (!par->empty() || layout->keepempty)) {
738                                 breakParagraph(params, paragraphs, par, pos,
739                                                layout->isEnvironment());
740                                 par = par->next();
741                                 pos = 0;
742                                 space_inserted = true;
743                         } else {
744                                 continue;
745                         }
746                         // do not insert consecutive spaces if !free_spacing
747                 } else if ((*cit == ' ' || *cit == '\t') &&
748                            space_inserted && !layout->free_spacing &&
749                                    !par->isFreeSpacing())
750                 {
751                         continue;
752                 } else if (*cit == '\t') {
753                         if (!layout->free_spacing && !par->isFreeSpacing()) {
754                                 // tabs are like spaces here
755                                 par->insertChar(pos, ' ', font);
756                                 ++pos;
757                                 space_inserted = true;
758                         } else {
759                                 const pos_type nb = 8 - pos % 8;
760                                 for (pos_type a = 0; a < nb ; ++a) {
761                                         par->insertChar(pos, ' ', font);
762                                         ++pos;
763                                 }
764                                 space_inserted = true;
765                         }
766                 } else if (!IsPrintable(*cit)) {
767                         // Ignore unprintables
768                         continue;
769                 } else {
770                         // just insert the character
771                         par->insertChar(pos, *cit, font);
772                         ++pos;
773                         space_inserted = (*cit == ' ');
774                 }
775
776         }
777 }
778
779
780 void Buffer::readInset(LyXLex & lex, ParagraphList::iterator pit,
781                        int & pos, LyXFont & font, Change current_change)
782 {
783         // consistency check
784         if (lex.getString() != "\\begin_inset") {
785                 lyxerr << "Buffer::readInset: Consistency check failed."
786                        << endl;
787         }
788
789         Inset * inset = 0;
790
791         lex.next();
792         string const tmptok = lex.getString();
793
794         // test the different insets
795         if (tmptok == "LatexCommand") {
796                 InsetCommandParams inscmd;
797                 inscmd.read(lex);
798
799                 string const cmdName = inscmd.getCmdName();
800
801                 // This strange command allows LyX to recognize "natbib" style
802                 // citations: citet, citep, Citet etc.
803                 if (compare_ascii_no_case(cmdName.substr(0,4), "cite") == 0) {
804                         inset = new InsetCitation(inscmd);
805                 } else if (cmdName == "bibitem") {
806                         lex.printError("Wrong place for bibitem");
807                         inset = new InsetBibitem(inscmd);
808                 } else if (cmdName == "BibTeX") {
809                         inset = new InsetBibtex(inscmd);
810                 } else if (cmdName == "index") {
811                         inset = new InsetIndex(inscmd);
812                 } else if (cmdName == "include") {
813                         inset = new InsetInclude(inscmd, *this);
814                 } else if (cmdName == "label") {
815                         inset = new InsetLabel(inscmd);
816                 } else if (cmdName == "url"
817                            || cmdName == "htmlurl") {
818                         inset = new InsetUrl(inscmd);
819                 } else if (cmdName == "ref"
820                            || cmdName == "pageref"
821                            || cmdName == "vref"
822                            || cmdName == "vpageref"
823                            || cmdName == "prettyref") {
824                         if (!inscmd.getOptions().empty()
825                             || !inscmd.getContents().empty()) {
826                                 inset = new InsetRef(inscmd, *this);
827                         }
828                 } else if (cmdName == "tableofcontents") {
829                         inset = new InsetTOC(inscmd);
830                 } else if (cmdName == "listofalgorithms") {
831                         inset = new InsetFloatList("algorithm");
832                 } else if (cmdName == "listoffigures") {
833                         inset = new InsetFloatList("figure");
834                 } else if (cmdName == "listoftables") {
835                         inset = new InsetFloatList("table");
836                 } else if (cmdName == "printindex") {
837                         inset = new InsetPrintIndex(inscmd);
838                 } else if (cmdName == "lyxparent") {
839                         inset = new InsetParent(inscmd, *this);
840                 }
841         } else {
842                 if (tmptok == "Quotes") {
843                         inset = new InsetQuotes;
844                 } else if (tmptok == "External") {
845                         inset = new InsetExternal;
846                 } else if (tmptok == "FormulaMacro") {
847                         inset = new InsetFormulaMacro;
848                 } else if (tmptok == "Formula") {
849                         inset = new InsetFormula;
850                 } else if (tmptok == "Graphics") {
851                         inset = new InsetGraphics;
852                 } else if (tmptok == "Note") {
853                         inset = new InsetNote(params);
854                 } else if (tmptok == "Include") {
855                         InsetCommandParams p("Include");
856                         inset = new InsetInclude(p, *this);
857                 } else if (tmptok == "ERT") {
858                         inset = new InsetERT(params);
859                 } else if (tmptok == "Tabular") {
860                         inset = new InsetTabular(*this);
861                 } else if (tmptok == "Text") {
862                         inset = new InsetText(params);
863                 } else if (tmptok == "Foot") {
864                         inset = new InsetFoot(params);
865                 } else if (tmptok == "Marginal") {
866                         inset = new InsetMarginal(params);
867                 } else if (tmptok == "OptArg") {
868                         inset = new InsetOptArg(params);
869                 } else if (tmptok == "Minipage") {
870                         inset = new InsetMinipage(params);
871                 } else if (tmptok == "Float") {
872                         lex.next();
873                         string tmptok = lex.getString();
874                         inset = new InsetFloat(params, tmptok);
875                 } else if (tmptok == "Wrap") {
876                         lex.next();
877                         string tmptok = lex.getString();
878                         inset = new InsetWrap(params, tmptok);
879 #if 0
880                 } else if (tmptok == "List") {
881                         inset = new InsetList;
882                 } else if (tmptok == "Theorem") {
883                         inset = new InsetList;
884 #endif
885                 } else if (tmptok == "Caption") {
886                         inset = new InsetCaption(params);
887                 } else if (tmptok == "FloatList") {
888                         inset = new InsetFloatList;
889                 }
890
891                 if (inset)
892                         inset->read(this, lex);
893         }
894
895         if (inset) {
896                 pit->insertInset(pos, inset, font, current_change);
897                 ++pos;
898         }
899 }
900
901
902 bool Buffer::readFile(LyXLex & lex, string const & filename)
903 {
904         return readFile(lex, filename, paragraphs.begin());
905 }
906
907
908 bool Buffer::readFile(LyXLex & lex, string const & filename, ParagraphList::iterator pit)
909 {
910         if (lex.isOK()) {
911                 lex.next();
912                 string const token(lex.getString());
913                 if (token == "\\lyxformat") { // the first token _must_ be...
914                         lex.eatLine();
915                         string tmp_format = lex.getString();
916                         //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
917                         // if present remove ".," from string.
918                         string::size_type dot = tmp_format.find_first_of(".,");
919                         //lyxerr << "           dot found at " << dot << endl;
920                         if (dot != string::npos)
921                                 tmp_format.erase(dot, 1);
922                         file_format = strToInt(tmp_format);
923                         //lyxerr << "format: " << file_format << endl;
924                         if (file_format == LYX_FORMAT) {
925                                 // current format
926                         } else if (file_format > LYX_FORMAT) {
927                                 // future format
928                                 Alert::alert(_("Warning!"),
929                                         _("The file was created with a newer version of "
930                                         "LyX. This is likely to cause problems."));
931
932                         } else if (file_format < LYX_FORMAT) {
933                                 // old formats
934                                 if (file_format < 200) {
935                                         Alert::alert(_("ERROR!"),
936                                                    _("Old LyX file format found. "
937                                                      "Use LyX 0.10.x to read this!"));
938                                         return false;
939                                 } else if (!filename.empty()) {
940                                         string command =
941                                                 LibFileSearch("lyx2lyx", "lyx2lyx");
942                                         if (command.empty()) {
943                                                 Alert::alert(_("ERROR!"),
944                                                              _("Can't find conversion script."));
945                                                 return false;
946                                         }
947                                         command += " -t"
948                                                 +tostr(LYX_FORMAT) + ' '
949                                                 + QuoteName(filename);
950                                         lyxerr[Debug::INFO] << "Running '"
951                                                             << command << '\''
952                                                             << endl;
953                                         cmd_ret const ret = RunCommand(command);
954                                         if (ret.first) {
955                                                 Alert::alert(_("ERROR!"),
956                                                              _("An error occured while "
957                                                                "running the conversion script."));
958                                                 return false;
959                                         }
960                                         istringstream is(STRCONV(ret.second));
961                                         LyXLex tmplex(0, 0);
962                                         tmplex.setStream(is);
963                                         return readFile(tmplex, string(), pit);
964                                 } else {
965                                         // This code is reached if lyx2lyx failed (for
966                                         // some reason) to change the file format of
967                                         // the file.
968                                         lyx::Assert(false);
969                                         return false;
970                                 }
971                         }
972                         bool the_end = readBody(lex, pit);
973                         params.setPaperStuff();
974
975                         if (!the_end) {
976                                 Alert::alert(_("Warning!"),
977                                            _("Reading of document is not complete"),
978                                            _("Maybe the document is truncated"));
979                         }
980                         return true;
981                 } else { // "\\lyxformat" not found
982                         Alert::alert(_("ERROR!"), _("Not a LyX file!"));
983                 }
984         } else
985                 Alert::alert(_("ERROR!"), _("Unable to read file!"));
986         return false;
987 }
988
989
990 // Should probably be moved to somewhere else: BufferView? LyXView?
991 bool Buffer::save() const
992 {
993         // We don't need autosaves in the immediate future. (Asger)
994         resetAutosaveTimers();
995
996         // make a backup
997         string s;
998         if (lyxrc.make_backup) {
999                 s = fileName() + '~';
1000                 if (!lyxrc.backupdir_path.empty())
1001                         s = AddName(lyxrc.backupdir_path,
1002                                     subst(os::slashify_path(s),'/','!'));
1003
1004                 // Rename is the wrong way of making a backup,
1005                 // this is the correct way.
1006                 /* truss cp fil fil2:
1007                    lstat("LyXVC3.lyx", 0xEFFFF898)                 Err#2 ENOENT
1008                    stat("LyXVC.lyx", 0xEFFFF688)                   = 0
1009                    open("LyXVC.lyx", O_RDONLY)                     = 3
1010                    open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
1011                    fstat(4, 0xEFFFF508)                            = 0
1012                    fstat(3, 0xEFFFF508)                            = 0
1013                    read(3, " # T h i s   f i l e   w".., 8192)     = 5579
1014                    write(4, " # T h i s   f i l e   w".., 5579)    = 5579
1015                    read(3, 0xEFFFD4A0, 8192)                       = 0
1016                    close(4)                                        = 0
1017                    close(3)                                        = 0
1018                    chmod("LyXVC3.lyx", 0100644)                    = 0
1019                    lseek(0, 0, SEEK_CUR)                           = 46440
1020                    _exit(0)
1021                 */
1022
1023                 // Should probably have some more error checking here.
1024                 // Doing it this way, also makes the inodes stay the same.
1025                 // This is still not a very good solution, in particular we
1026                 // might loose the owner of the backup.
1027                 FileInfo finfo(fileName());
1028                 if (finfo.exist()) {
1029                         mode_t fmode = finfo.getMode();
1030                         struct utimbuf times = {
1031                                 finfo.getAccessTime(),
1032                                 finfo.getModificationTime() };
1033
1034                         ifstream ifs(fileName().c_str());
1035                         ofstream ofs(s.c_str(), ios::out|ios::trunc);
1036                         if (ifs && ofs) {
1037                                 ofs << ifs.rdbuf();
1038                                 ifs.close();
1039                                 ofs.close();
1040                                 ::chmod(s.c_str(), fmode);
1041
1042                                 if (::utime(s.c_str(), &times)) {
1043                                         lyxerr << "utime error." << endl;
1044                                 }
1045                         } else {
1046                                 lyxerr << "LyX was not able to make "
1047                                         "backup copy. Beware." << endl;
1048                         }
1049                 }
1050         }
1051
1052         if (writeFile(fileName())) {
1053                 markClean();
1054                 removeAutosaveFile(fileName());
1055         } else {
1056                 // Saving failed, so backup is not backup
1057                 if (lyxrc.make_backup) {
1058                         lyx::rename(s, fileName());
1059                 }
1060                 return false;
1061         }
1062         return true;
1063 }
1064
1065
1066 bool Buffer::writeFile(string const & fname) const
1067 {
1068         if (read_only && (fname == fileName())) {
1069                 return false;
1070         }
1071
1072         FileInfo finfo(fname);
1073         if (finfo.exist() && !finfo.writable()) {
1074                 return false;
1075         }
1076
1077         ofstream ofs(fname.c_str());
1078         if (!ofs) {
1079                 return false;
1080         }
1081
1082 #ifdef HAVE_LOCALE
1083         // Use the standard "C" locale for file output.
1084         ofs.imbue(std::locale::classic());
1085 #endif
1086
1087         // The top of the file should not be written by params.
1088
1089         // write out a comment in the top of the file
1090         ofs << '#' << lyx_docversion
1091             << " created this file. For more info see http://www.lyx.org/\n"
1092             << "\\lyxformat " << LYX_FORMAT << "\n";
1093
1094         // now write out the buffer paramters.
1095         params.writeFile(ofs);
1096
1097         ofs << "\\end_header\n";
1098
1099         Paragraph::depth_type depth = 0;
1100
1101         // this will write out all the paragraphs
1102         // using recursive descent.
1103         ParagraphList::iterator pit = paragraphs.begin();
1104         ParagraphList::iterator pend = paragraphs.end();
1105         for (; pit != pend; ++pit)
1106                 pit->write(this, ofs, params, depth);
1107
1108         // Write marker that shows file is complete
1109         ofs << "\n\\the_end" << endl;
1110
1111         ofs.close();
1112
1113         // how to check if close went ok?
1114         // Following is an attempt... (BE 20001011)
1115
1116         // good() returns false if any error occured, including some
1117         //        formatting error.
1118         // bad()  returns true if something bad happened in the buffer,
1119         //        which should include file system full errors.
1120
1121         bool status = true;
1122         if (!ofs.good()) {
1123                 status = false;
1124 #if 0
1125                 if (ofs.bad()) {
1126                         lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1127                 } else {
1128                         lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1129                                << endl;
1130                 }
1131 #endif
1132         }
1133
1134         return status;
1135 }
1136
1137
1138 namespace {
1139
1140 pair<int, string> const addDepth(int depth, int ldepth)
1141 {
1142         int d = depth * 2;
1143         if (ldepth > depth)
1144                 d += (ldepth - depth) * 2;
1145         return make_pair(d, string(d, ' '));
1146 }
1147
1148 }
1149
1150
1151 string const Buffer::asciiParagraph(Paragraph const & par,
1152                                     unsigned int linelen,
1153                                     bool noparbreak) const
1154 {
1155         ostringstream buffer;
1156         Paragraph::depth_type depth = 0;
1157         int ltype = 0;
1158         Paragraph::depth_type ltype_depth = 0;
1159         bool ref_printed = false;
1160 //      if (!par->previous()) {
1161 #if 0
1162         // begins or ends a deeper area ?
1163         if (depth != par->params().depth()) {
1164                 if (par->params().depth() > depth) {
1165                         while (par->params().depth() > depth) {
1166                                 ++depth;
1167                         }
1168                 } else {
1169                         while (par->params().depth() < depth) {
1170                                 --depth;
1171                         }
1172                 }
1173         }
1174 #else
1175         depth = par.params().depth();
1176 #endif
1177
1178         // First write the layout
1179         string const & tmp = par.layout()->name();
1180         if (compare_no_case(tmp, "itemize") == 0) {
1181                 ltype = 1;
1182                 ltype_depth = depth + 1;
1183         } else if (compare_ascii_no_case(tmp, "enumerate") == 0) {
1184                 ltype = 2;
1185                 ltype_depth = depth + 1;
1186         } else if (contains(ascii_lowercase(tmp), "ection")) {
1187                 ltype = 3;
1188                 ltype_depth = depth + 1;
1189         } else if (contains(ascii_lowercase(tmp), "aragraph")) {
1190                 ltype = 4;
1191                 ltype_depth = depth + 1;
1192         } else if (compare_ascii_no_case(tmp, "description") == 0) {
1193                 ltype = 5;
1194                 ltype_depth = depth + 1;
1195         } else if (compare_ascii_no_case(tmp, "abstract") == 0) {
1196                 ltype = 6;
1197                 ltype_depth = 0;
1198         } else if (compare_ascii_no_case(tmp, "bibliography") == 0) {
1199                 ltype = 7;
1200                 ltype_depth = 0;
1201         } else {
1202                 ltype = 0;
1203                 ltype_depth = 0;
1204         }
1205
1206         /* maybe some vertical spaces */
1207
1208         /* the labelwidthstring used in lists */
1209
1210         /* some lines? */
1211
1212         /* some pagebreaks? */
1213
1214         /* noindent ? */
1215
1216         /* what about the alignment */
1217 //      } else {
1218 //              lyxerr << "Should this ever happen?" << endl;
1219 //      }
1220
1221         // linelen <= 0 is special and means we don't have paragraph breaks
1222
1223         string::size_type currlinelen = 0;
1224
1225         if (!noparbreak) {
1226                 if (linelen > 0)
1227                         buffer << "\n\n";
1228
1229                 buffer << string(depth * 2, ' ');
1230                 currlinelen += depth * 2;
1231
1232                 //--
1233                 // we should probably change to the paragraph language in the
1234                 // gettext here (if possible) so that strings are outputted in
1235                 // the correct language! (20012712 Jug)
1236                 //--
1237                 switch (ltype) {
1238                 case 0: // Standard
1239                 case 4: // (Sub)Paragraph
1240                 case 5: // Description
1241                         break;
1242                 case 6: // Abstract
1243                         if (linelen > 0) {
1244                                 buffer << _("Abstract") << "\n\n";
1245                                 currlinelen = 0;
1246                         } else {
1247                                 string const abst = _("Abstract: ");
1248                                 buffer << abst;
1249                                 currlinelen += abst.length();
1250                         }
1251                         break;
1252                 case 7: // Bibliography
1253                         if (!ref_printed) {
1254                                 if (linelen > 0) {
1255                                         buffer << _("References") << "\n\n";
1256                                         currlinelen = 0;
1257                                 } else {
1258                                         string const refs = _("References: ");
1259                                         buffer << refs;
1260                                         currlinelen += refs.length();
1261                                 }
1262
1263                                 ref_printed = true;
1264                         }
1265                         break;
1266                 default:
1267                 {
1268                         string const parlab = par.params().labelString();
1269                         buffer << parlab << ' ';
1270                         currlinelen += parlab.length() + 1;
1271                 }
1272                 break;
1273
1274                 }
1275         }
1276
1277         if (!currlinelen) {
1278                 pair<int, string> p = addDepth(depth, ltype_depth);
1279                 buffer << p.second;
1280                 currlinelen += p.first;
1281         }
1282
1283         // this is to change the linebreak to do it by word a bit more
1284         // intelligent hopefully! (only in the case where we have a
1285         // max linelength!) (Jug)
1286
1287         string word;
1288
1289         for (pos_type i = 0; i < par.size(); ++i) {
1290                 char c = par.getUChar(params, i);
1291                 switch (c) {
1292                 case Paragraph::META_INSET:
1293                 {
1294                         Inset const * inset = par.getInset(i);
1295                         if (inset) {
1296                                 if (linelen > 0) {
1297                                         buffer << word;
1298                                         currlinelen += word.length();
1299                                         word.erase();
1300                                 }
1301                                 if (inset->ascii(this, buffer, linelen)) {
1302                                         // to be sure it breaks paragraph
1303                                         currlinelen += linelen;
1304                                 }
1305                         }
1306                 }
1307                 break;
1308
1309                 case Paragraph::META_NEWLINE:
1310                         if (linelen > 0) {
1311                                 buffer << word << "\n";
1312                                 word.erase();
1313
1314                                 pair<int, string> p = addDepth(depth,
1315                                                                ltype_depth);
1316                                 buffer << p.second;
1317                                 currlinelen = p.first;
1318                         }
1319                         break;
1320
1321                 default:
1322                         if (c == ' ') {
1323                                 if (linelen > 0 &&
1324                                     currlinelen + word.length() > linelen - 10) {
1325                                         buffer << "\n";
1326                                         pair<int, string> p = addDepth(depth, ltype_depth);
1327                                         buffer << p.second;
1328                                         currlinelen = p.first;
1329                                 }
1330
1331                                 buffer << word << ' ';
1332                                 currlinelen += word.length() + 1;
1333                                 word.erase();
1334
1335                         } else {
1336                                 if (c != '\0') {
1337                                         word += c;
1338                                 } else {
1339                                         lyxerr[Debug::INFO] <<
1340                                                 "writeAsciiFile: NULL char in structure." << endl;
1341                                 }
1342                                 if ((linelen > 0) &&
1343                                         (currlinelen + word.length()) > linelen)
1344                                 {
1345                                         buffer << "\n";
1346
1347                                         pair<int, string> p =
1348                                                 addDepth(depth, ltype_depth);
1349                                         buffer << p.second;
1350                                         currlinelen = p.first;
1351                                 }
1352                         }
1353                         break;
1354                 }
1355         }
1356         buffer << word;
1357         return STRCONV(buffer.str());
1358 }
1359
1360
1361 void Buffer::writeFileAscii(string const & fname, int linelen)
1362 {
1363         ofstream ofs(fname.c_str());
1364         if (!ofs) {
1365                 Alert::err_alert(_("Error: Cannot write file:"), fname);
1366                 return;
1367         }
1368         writeFileAscii(ofs, linelen);
1369 }
1370
1371
1372 void Buffer::writeFileAscii(ostream & os, int linelen)
1373 {
1374         ParagraphList::iterator beg = paragraphs.begin();
1375         ParagraphList::iterator end = paragraphs.end();
1376         ParagraphList::iterator it = beg;
1377         for (; it != end; ++it) {
1378                 os << asciiParagraph(*it, linelen, it == beg);
1379         }
1380         os << "\n";
1381 }
1382
1383
1384
1385 void Buffer::makeLaTeXFile(string const & fname,
1386                            string const & original_path,
1387                            bool nice, bool only_body, bool only_preamble)
1388 {
1389         lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
1390
1391         ofstream ofs(fname.c_str());
1392         if (!ofs) {
1393                 Alert::err_alert(_("Error: Cannot open file: "), fname);
1394                 return;
1395         }
1396
1397         makeLaTeXFile(ofs, original_path, nice, only_body, only_preamble);
1398
1399         ofs.close();
1400         if (ofs.fail()) {
1401                 lyxerr << "File was not closed properly." << endl;
1402         }
1403 }
1404
1405
1406 void Buffer::makeLaTeXFile(ostream & os,
1407                            string const & original_path,
1408                            bool nice, bool only_body, bool only_preamble)
1409 {
1410         niceFile = nice; // this will be used by Insetincludes.
1411
1412         // validate the buffer.
1413         lyxerr[Debug::LATEX] << "  Validating buffer..." << endl;
1414         LaTeXFeatures features(params);
1415         validate(features);
1416         lyxerr[Debug::LATEX] << "  Buffer validation done." << endl;
1417
1418         texrow.reset();
1419         // The starting paragraph of the coming rows is the
1420         // first paragraph of the document. (Asger)
1421         texrow.start(&*(paragraphs.begin()), 0);
1422
1423         if (!only_body && nice) {
1424                 os << "%% " << lyx_docversion << " created this file.  "
1425                         "For more info, see http://www.lyx.org/.\n"
1426                         "%% Do not edit unless you really know what "
1427                         "you are doing.\n";
1428                 texrow.newline();
1429                 texrow.newline();
1430         }
1431         lyxerr[Debug::INFO] << "lyx header finished" << endl;
1432         // There are a few differences between nice LaTeX and usual files:
1433         // usual is \batchmode and has a
1434         // special input@path to allow the including of figures
1435         // with either \input or \includegraphics (what figinsets do).
1436         // input@path is set when the actual parameter
1437         // original_path is set. This is done for usual tex-file, but not
1438         // for nice-latex-file. (Matthias 250696)
1439         if (!only_body) {
1440                 if (!nice) {
1441                         // code for usual, NOT nice-latex-file
1442                         os << "\\batchmode\n"; // changed
1443                         // from \nonstopmode
1444                         texrow.newline();
1445                 }
1446                 if (!original_path.empty()) {
1447                         string inputpath = os::external_path(original_path);
1448                         subst(inputpath, "~", "\\string~");
1449                         os << "\\makeatletter\n"
1450                             << "\\def\\input@path{{"
1451                             << inputpath << "/}}\n"
1452                             << "\\makeatother\n";
1453                         texrow.newline();
1454                         texrow.newline();
1455                         texrow.newline();
1456                 }
1457
1458                 // Write the preamble
1459                 params.writeLaTeX(os, features, texrow);
1460
1461                 if (only_preamble)
1462                         return;
1463
1464                 // make the body.
1465                 os << "\\begin{document}\n";
1466                 texrow.newline();
1467         } // only_body
1468         lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
1469
1470         if (!lyxrc.language_auto_begin) {
1471                 os << subst(lyxrc.language_command_begin, "$$lang",
1472                              params.language->babel())
1473                     << endl;
1474                 texrow.newline();
1475         }
1476
1477         latexParagraphs(this, paragraphs, paragraphs.begin(), paragraphs.end(), os, texrow);
1478
1479         // add this just in case after all the paragraphs
1480         os << endl;
1481         texrow.newline();
1482
1483         if (!lyxrc.language_auto_end) {
1484                 os << subst(lyxrc.language_command_end, "$$lang",
1485                              params.language->babel())
1486                     << endl;
1487                 texrow.newline();
1488         }
1489
1490         if (!only_body) {
1491                 os << "\\end{document}\n";
1492                 texrow.newline();
1493
1494                 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
1495         } else {
1496                 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
1497                                      << endl;
1498         }
1499
1500         // Just to be sure. (Asger)
1501         texrow.newline();
1502
1503         lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
1504         lyxerr[Debug::INFO] << "Row count was " << texrow.rows() - 1
1505                             << '.' << endl;
1506
1507         // we want this to be true outside previews (for insetexternal)
1508         niceFile = true;
1509 }
1510
1511
1512 bool Buffer::isLatex() const
1513 {
1514         return params.getLyXTextClass().outputType() == LATEX;
1515 }
1516
1517
1518 bool Buffer::isLinuxDoc() const
1519 {
1520         return params.getLyXTextClass().outputType() == LINUXDOC;
1521 }
1522
1523
1524 bool Buffer::isLiterate() const
1525 {
1526         return params.getLyXTextClass().outputType() == LITERATE;
1527 }
1528
1529
1530 bool Buffer::isDocBook() const
1531 {
1532         return params.getLyXTextClass().outputType() == DOCBOOK;
1533 }
1534
1535
1536 bool Buffer::isSGML() const
1537 {
1538         LyXTextClass const & tclass = params.getLyXTextClass();
1539
1540         return tclass.outputType() == LINUXDOC ||
1541                tclass.outputType() == DOCBOOK;
1542 }
1543
1544
1545 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
1546 {
1547         ofstream ofs(fname.c_str());
1548
1549         if (!ofs) {
1550                 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
1551                 return;
1552         }
1553
1554         niceFile = nice; // this will be used by included files.
1555
1556         LaTeXFeatures features(params);
1557
1558         validate(features);
1559
1560         texrow.reset();
1561
1562         LyXTextClass const & tclass = params.getLyXTextClass();
1563
1564         string top_element = tclass.latexname();
1565
1566         if (!body_only) {
1567                 ofs << "<!doctype linuxdoc system";
1568
1569                 string preamble = params.preamble;
1570                 const string name = nice ? ChangeExtension(filename_, ".sgml")
1571                          : fname;
1572                 preamble += features.getIncludedFiles(name);
1573                 preamble += features.getLyXSGMLEntities();
1574
1575                 if (!preamble.empty()) {
1576                         ofs << " [ " << preamble << " ]";
1577                 }
1578                 ofs << ">\n\n";
1579
1580                 if (params.options.empty())
1581                         sgml::openTag(ofs, 0, false, top_element);
1582                 else {
1583                         string top = top_element;
1584                         top += ' ';
1585                         top += params.options;
1586                         sgml::openTag(ofs, 0, false, top);
1587                 }
1588         }
1589
1590         ofs << "<!-- "  << lyx_docversion
1591             << " created this file. For more info see http://www.lyx.org/"
1592             << " -->\n";
1593
1594         Paragraph::depth_type depth = 0; // paragraph depth
1595         Paragraph * par = &*(paragraphs.begin());
1596         string item_name;
1597         vector<string> environment_stack(5);
1598
1599         while (par) {
1600                 LyXLayout_ptr const & style = par->layout();
1601                 // treat <toc> as a special case for compatibility with old code
1602                 if (par->isInset(0)) {
1603                         Inset * inset = par->getInset(0);
1604                         Inset::Code lyx_code = inset->lyxCode();
1605                         if (lyx_code == Inset::TOC_CODE) {
1606                                 string const temp = "toc";
1607                                 sgml::openTag(ofs, depth, false, temp);
1608
1609                                 par = par->next();
1610                                 continue;
1611                         }
1612                 }
1613
1614                 // environment tag closing
1615                 for (; depth > par->params().depth(); --depth) {
1616                         sgml::closeTag(ofs, depth, false, environment_stack[depth]);
1617                         environment_stack[depth].erase();
1618                 }
1619
1620                 // write opening SGML tags
1621                 switch (style->latextype) {
1622                 case LATEX_PARAGRAPH:
1623                         if (depth == par->params().depth()
1624                            && !environment_stack[depth].empty()) {
1625                                 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
1626                                 environment_stack[depth].erase();
1627                                 if (depth)
1628                                         --depth;
1629                                 else
1630                                         ofs << "</p>";
1631                         }
1632                         sgml::openTag(ofs, depth, false, style->latexname());
1633                         break;
1634
1635                 case LATEX_COMMAND:
1636                         if (depth!= 0)
1637                                 sgmlError(par, 0,
1638                                           _("Error: Wrong depth for LatexType Command.\n"));
1639
1640                         if (!environment_stack[depth].empty()) {
1641                                 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
1642                                 ofs << "</p>";
1643                         }
1644
1645                         environment_stack[depth].erase();
1646                         sgml::openTag(ofs, depth, false, style->latexname());
1647                         break;
1648
1649                 case LATEX_ENVIRONMENT:
1650                 case LATEX_ITEM_ENVIRONMENT:
1651                 case LATEX_BIB_ENVIRONMENT:
1652                 {
1653                         string const & latexname = style->latexname();
1654
1655                         if (depth == par->params().depth()
1656                             && environment_stack[depth] != latexname) {
1657                                 sgml::closeTag(ofs, depth, false,
1658                                              environment_stack[depth]);
1659                                 environment_stack[depth].erase();
1660                         }
1661                         if (depth < par->params().depth()) {
1662                                depth = par->params().depth();
1663                                environment_stack[depth].erase();
1664                         }
1665                         if (environment_stack[depth] != latexname) {
1666                                 if (depth == 0) {
1667                                         sgml::openTag(ofs, depth, false, "p");
1668                                 }
1669                                 sgml::openTag(ofs, depth, false, latexname);
1670
1671                                 if (environment_stack.size() == depth + 1)
1672                                         environment_stack.push_back("!-- --");
1673                                 environment_stack[depth] = latexname;
1674                         }
1675
1676                         if (style->latexparam() == "CDATA")
1677                                 ofs << "<![CDATA[";
1678
1679                         if (style->latextype == LATEX_ENVIRONMENT) break;
1680
1681                         if (style->labeltype == LABEL_MANUAL)
1682                                 item_name = "tag";
1683                         else
1684                                 item_name = "item";
1685
1686                         sgml::openTag(ofs, depth + 1, false, item_name);
1687                 }
1688                 break;
1689
1690                 default:
1691                         sgml::openTag(ofs, depth, false, style->latexname());
1692                         break;
1693                 }
1694
1695                 simpleLinuxDocOnePar(ofs, par, depth);
1696
1697                 par = par->next();
1698
1699                 ofs << "\n";
1700                 // write closing SGML tags
1701                 switch (style->latextype) {
1702                 case LATEX_COMMAND:
1703                         break;
1704                 case LATEX_ENVIRONMENT:
1705                 case LATEX_ITEM_ENVIRONMENT:
1706                 case LATEX_BIB_ENVIRONMENT:
1707                         if (style->latexparam() == "CDATA")
1708                                 ofs << "]]>";
1709                         break;
1710                 default:
1711                         sgml::closeTag(ofs, depth, false, style->latexname());
1712                         break;
1713                 }
1714         }
1715
1716         // Close open tags
1717         for (int i = depth; i >= 0; --i)
1718                 sgml::closeTag(ofs, depth, false, environment_stack[i]);
1719
1720         if (!body_only) {
1721                 ofs << "\n\n";
1722                 sgml::closeTag(ofs, 0, false, top_element);
1723         }
1724
1725         ofs.close();
1726         // How to check for successful close
1727
1728         // we want this to be true outside previews (for insetexternal)
1729         niceFile = true;
1730 }
1731
1732
1733 // checks, if newcol chars should be put into this line
1734 // writes newline, if necessary.
1735 namespace {
1736
1737 void sgmlLineBreak(ostream & os, string::size_type & colcount,
1738                           string::size_type newcol)
1739 {
1740         colcount += newcol;
1741         if (colcount > lyxrc.ascii_linelen) {
1742                 os << "\n";
1743                 colcount = newcol; // assume write after this call
1744         }
1745 }
1746
1747 enum PAR_TAG {
1748         NONE=0,
1749         TT = 1,
1750         SF = 2,
1751         BF = 4,
1752         IT = 8,
1753         SL = 16,
1754         EM = 32
1755 };
1756
1757
1758 string tag_name(PAR_TAG const & pt) {
1759         switch (pt) {
1760         case NONE: return "!-- --";
1761         case TT: return "tt";
1762         case SF: return "sf";
1763         case BF: return "bf";
1764         case IT: return "it";
1765         case SL: return "sl";
1766         case EM: return "em";
1767         }
1768         return "";
1769 }
1770
1771
1772 inline
1773 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
1774 {
1775         p1 = static_cast<PAR_TAG>(p1 | p2);
1776 }
1777
1778
1779 inline
1780 void reset(PAR_TAG & p1, PAR_TAG const & p2)
1781 {
1782         p1 = static_cast<PAR_TAG>(p1 & ~p2);
1783 }
1784
1785 } // anon
1786
1787
1788 // Handle internal paragraph parsing -- layout already processed.
1789 void Buffer::simpleLinuxDocOnePar(ostream & os,
1790         Paragraph * par,
1791         Paragraph::depth_type /*depth*/)
1792 {
1793         LyXLayout_ptr const & style = par->layout();
1794
1795         string::size_type char_line_count = 5;     // Heuristic choice ;-)
1796
1797         // gets paragraph main font
1798         LyXFont font_old;
1799         bool desc_on;
1800         if (style->labeltype == LABEL_MANUAL) {
1801                 font_old = style->labelfont;
1802                 desc_on = true;
1803         } else {
1804                 font_old = style->font;
1805                 desc_on = false;
1806         }
1807
1808         LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
1809         LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
1810         LyXFont::FONT_SHAPE  shape_type  = LyXFont::UP_SHAPE;
1811         bool is_em = false;
1812
1813         stack<PAR_TAG> tag_state;
1814         // parsing main loop
1815         for (pos_type i = 0; i < par->size(); ++i) {
1816
1817                 PAR_TAG tag_close = NONE;
1818                 list < PAR_TAG > tag_open;
1819
1820                 LyXFont const font = par->getFont(params, i);
1821
1822                 if (font_old.family() != font.family()) {
1823                         switch (family_type) {
1824                         case LyXFont::SANS_FAMILY:
1825                                 tag_close |= SF;
1826                                 break;
1827                         case LyXFont::TYPEWRITER_FAMILY:
1828                                 tag_close |= TT;
1829                                 break;
1830                         default:
1831                                 break;
1832                         }
1833
1834                         family_type = font.family();
1835
1836                         switch (family_type) {
1837                         case LyXFont::SANS_FAMILY:
1838                                 tag_open.push_back(SF);
1839                                 break;
1840                         case LyXFont::TYPEWRITER_FAMILY:
1841                                 tag_open.push_back(TT);
1842                                 break;
1843                         default:
1844                                 break;
1845                         }
1846                 }
1847
1848                 if (font_old.series() != font.series()) {
1849                         switch (series_type) {
1850                         case LyXFont::BOLD_SERIES:
1851                                 tag_close |= BF;
1852                                 break;
1853                         default:
1854                                 break;
1855                         }
1856
1857                         series_type = font.series();
1858
1859                         switch (series_type) {
1860                         case LyXFont::BOLD_SERIES:
1861                                 tag_open.push_back(BF);
1862                                 break;
1863                         default:
1864                                 break;
1865                         }
1866
1867                 }
1868
1869                 if (font_old.shape() != font.shape()) {
1870                         switch (shape_type) {
1871                         case LyXFont::ITALIC_SHAPE:
1872                                 tag_close |= IT;
1873                                 break;
1874                         case LyXFont::SLANTED_SHAPE:
1875                                 tag_close |= SL;
1876                                 break;
1877                         default:
1878                                 break;
1879                         }
1880
1881                         shape_type = font.shape();
1882
1883                         switch (shape_type) {
1884                         case LyXFont::ITALIC_SHAPE:
1885                                 tag_open.push_back(IT);
1886                                 break;
1887                         case LyXFont::SLANTED_SHAPE:
1888                                 tag_open.push_back(SL);
1889                                 break;
1890                         default:
1891                                 break;
1892                         }
1893                 }
1894                 // handle <em> tag
1895                 if (font_old.emph() != font.emph()) {
1896                         if (font.emph() == LyXFont::ON) {
1897                                 tag_open.push_back(EM);
1898                                 is_em = true;
1899                         }
1900                         else if (is_em) {
1901                                 tag_close |= EM;
1902                                 is_em = false;
1903                         }
1904                 }
1905
1906                 list < PAR_TAG > temp;
1907                 while (!tag_state.empty() && tag_close) {
1908                         PAR_TAG k =  tag_state.top();
1909                         tag_state.pop();
1910                         os << "</" << tag_name(k) << '>';
1911                         if (tag_close & k)
1912                                 reset(tag_close,k);
1913                         else
1914                                 temp.push_back(k);
1915                 }
1916
1917                 for(list< PAR_TAG >::const_iterator j = temp.begin();
1918                     j != temp.end(); ++j) {
1919                         tag_state.push(*j);
1920                         os << '<' << tag_name(*j) << '>';
1921                 }
1922
1923                 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
1924                     j != tag_open.end(); ++j) {
1925                         tag_state.push(*j);
1926                         os << '<' << tag_name(*j) << '>';
1927                 }
1928
1929                 char c = par->getChar(i);
1930
1931                 if (c == Paragraph::META_INSET) {
1932                         Inset * inset = par->getInset(i);
1933                         inset->linuxdoc(this, os);
1934                         font_old = font;
1935                         continue;
1936                 }
1937
1938                 if (style->latexparam() == "CDATA") {
1939                         // "TeX"-Mode on == > SGML-Mode on.
1940                         if (c != '\0')
1941                                 os << c;
1942                         ++char_line_count;
1943                 } else {
1944                         bool ws;
1945                         string str;
1946                         boost::tie(ws, str) = sgml::escapeChar(c);
1947                         if (ws && !style->free_spacing && !par->isFreeSpacing()) {
1948                                 // in freespacing mode, spaces are
1949                                 // non-breaking characters
1950                                 if (desc_on) {// if char is ' ' then...
1951
1952                                         ++char_line_count;
1953                                         sgmlLineBreak(os, char_line_count, 6);
1954                                         os << "</tag>";
1955                                         desc_on = false;
1956                                 } else  {
1957                                         sgmlLineBreak(os, char_line_count, 1);
1958                                         os << c;
1959                                 }
1960                         } else {
1961                                 os << str;
1962                                 char_line_count += str.length();
1963                         }
1964                 }
1965                 font_old = font;
1966         }
1967
1968         while (!tag_state.empty()) {
1969                 os << "</" << tag_name(tag_state.top()) << '>';
1970                 tag_state.pop();
1971         }
1972
1973         // resets description flag correctly
1974         if (desc_on) {
1975                 // <tag> not closed...
1976                 sgmlLineBreak(os, char_line_count, 6);
1977                 os << "</tag>";
1978         }
1979 }
1980
1981
1982 // Print an error message.
1983 void Buffer::sgmlError(Paragraph * /*par*/, int /*pos*/,
1984         string const & /*message*/) const
1985 {
1986 #ifdef WITH_WARNINGS
1987 #warning This is wrong we cannot insert an inset like this!!!
1988         // I guess this was Jose' so I explain you more or less why this
1989         // is wrong. This way you insert something in the paragraph and
1990         // don't tell it to LyXText (row rebreaking and undo handling!!!)
1991         // I deactivate this code, have a look at BufferView::insertErrors
1992         // how you should do this correctly! (Jug 20020315)
1993 #endif
1994 #if 0
1995         // insert an error marker in text
1996         InsetError * new_inset = new InsetError(message);
1997         par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
1998                          params.language));
1999 #endif
2000 }
2001
2002
2003 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
2004 {
2005         ofstream ofs(fname.c_str());
2006         if (!ofs) {
2007                 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2008                 return;
2009         }
2010
2011         Paragraph * par = &*(paragraphs.begin());
2012
2013         niceFile = nice; // this will be used by Insetincludes.
2014
2015         LaTeXFeatures features(params);
2016         validate(features);
2017
2018         texrow.reset();
2019
2020         LyXTextClass const & tclass = params.getLyXTextClass();
2021         string top_element = tclass.latexname();
2022
2023         if (!only_body) {
2024                 ofs << "<!DOCTYPE " << top_element
2025                     << "  PUBLIC \"-//OASIS//DTD DocBook V4.1//EN\"";
2026
2027                 string preamble = params.preamble;
2028                 const string name = nice ? ChangeExtension(filename_, ".sgml")
2029                          : fname;
2030                 preamble += features.getIncludedFiles(name);
2031                 preamble += features.getLyXSGMLEntities();
2032
2033                 if (!preamble.empty()) {
2034                         ofs << "\n [ " << preamble << " ]";
2035                 }
2036                 ofs << ">\n\n";
2037         }
2038
2039         string top = top_element;
2040         top += " lang=\"";
2041         top += params.language->code();
2042         top += '"';
2043
2044         if (!params.options.empty()) {
2045                 top += ' ';
2046                 top += params.options;
2047         }
2048         sgml::openTag(ofs, 0, false, top);
2049
2050         ofs << "<!-- DocBook file was created by " << lyx_docversion
2051             << "\n  See http://www.lyx.org/ for more information -->\n";
2052
2053         vector<string> environment_stack(10);
2054         vector<string> environment_inner(10);
2055         vector<string> command_stack(10);
2056
2057         bool command_flag = false;
2058         Paragraph::depth_type command_depth = 0;
2059         Paragraph::depth_type command_base = 0;
2060         Paragraph::depth_type cmd_depth = 0;
2061         Paragraph::depth_type depth = 0; // paragraph depth
2062
2063         string item_name;
2064         string command_name;
2065
2066         while (par) {
2067                 string sgmlparam;
2068                 string c_depth;
2069                 string c_params;
2070                 int desc_on = 0; // description mode
2071
2072                 LyXLayout_ptr const & style = par->layout();
2073
2074                 // environment tag closing
2075                 for (; depth > par->params().depth(); --depth) {
2076                         if (environment_inner[depth] != "!-- --") {
2077                                 item_name = "listitem";
2078                                 sgml::closeTag(ofs, command_depth + depth, false, item_name);
2079                                 if (environment_inner[depth] == "varlistentry")
2080                                         sgml::closeTag(ofs, depth+command_depth, false, environment_inner[depth]);
2081                         }
2082                         sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2083                         environment_stack[depth].erase();
2084                         environment_inner[depth].erase();
2085                 }
2086
2087                 if (depth == par->params().depth()
2088                    && environment_stack[depth] != style->latexname()
2089                    && !environment_stack[depth].empty()) {
2090                         if (environment_inner[depth] != "!-- --") {
2091                                 item_name= "listitem";
2092                                 sgml::closeTag(ofs, command_depth+depth, false, item_name);
2093                                 if (environment_inner[depth] == "varlistentry")
2094                                         sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
2095                         }
2096
2097                         sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2098
2099                         environment_stack[depth].erase();
2100                         environment_inner[depth].erase();
2101                 }
2102
2103                 // Write opening SGML tags.
2104                 switch (style->latextype) {
2105                 case LATEX_PARAGRAPH:
2106                         sgml::openTag(ofs, depth + command_depth,
2107                                     false, style->latexname());
2108                         break;
2109
2110                 case LATEX_COMMAND:
2111                         if (depth != 0)
2112                                 sgmlError(par, 0,
2113                                           _("Error: Wrong depth for LatexType Command.\n"));
2114
2115                         command_name = style->latexname();
2116
2117                         sgmlparam = style->latexparam();
2118                         c_params = split(sgmlparam, c_depth,'|');
2119
2120                         cmd_depth = lyx::atoi(c_depth);
2121
2122                         if (command_flag) {
2123                                 if (cmd_depth < command_base) {
2124                                         for (Paragraph::depth_type j = command_depth;
2125                                              j >= command_base; --j) {
2126                                                 sgml::closeTag(ofs, j, false, command_stack[j]);
2127                                                 ofs << endl;
2128                                         }
2129                                         command_depth = command_base = cmd_depth;
2130                                 } else if (cmd_depth <= command_depth) {
2131                                         for (int j = command_depth;
2132                                              j >= int(cmd_depth); --j) {
2133                                                 sgml::closeTag(ofs, j, false, command_stack[j]);
2134                                                 ofs << endl;
2135                                         }
2136                                         command_depth = cmd_depth;
2137                                 } else
2138                                         command_depth = cmd_depth;
2139                         } else {
2140                                 command_depth = command_base = cmd_depth;
2141                                 command_flag = true;
2142                         }
2143                         if (command_stack.size() == command_depth + 1)
2144                                 command_stack.push_back(string());
2145                         command_stack[command_depth] = command_name;
2146
2147                         // treat label as a special case for
2148                         // more WYSIWYM handling.
2149                         // This is a hack while paragraphs can't have
2150                         // attributes, like id in this case.
2151                         if (par->isInset(0)) {
2152                                 Inset * inset = par->getInset(0);
2153                                 Inset::Code lyx_code = inset->lyxCode();
2154                                 if (lyx_code == Inset::LABEL_CODE) {
2155                                         command_name += " id=\"";
2156                                         command_name += (static_cast<InsetCommand *>(inset))->getContents();
2157                                         command_name += '"';
2158                                         desc_on = 3;
2159                                 }
2160                         }
2161
2162                         sgml::openTag(ofs, depth + command_depth, false, command_name);
2163
2164                         item_name = c_params.empty() ? "title" : c_params;
2165                         sgml::openTag(ofs, depth + 1 + command_depth, false, item_name);
2166                         break;
2167
2168                 case LATEX_ENVIRONMENT:
2169                 case LATEX_ITEM_ENVIRONMENT:
2170                         if (depth < par->params().depth()) {
2171                                 depth = par->params().depth();
2172                                 environment_stack[depth].erase();
2173                         }
2174
2175                         if (environment_stack[depth] != style->latexname()) {
2176                                 if (environment_stack.size() == depth + 1) {
2177                                         environment_stack.push_back("!-- --");
2178                                         environment_inner.push_back("!-- --");
2179                                 }
2180                                 environment_stack[depth] = style->latexname();
2181                                 environment_inner[depth] = "!-- --";
2182                                 sgml::openTag(ofs, depth + command_depth, false, environment_stack[depth]);
2183                         } else {
2184                                 if (environment_inner[depth] != "!-- --") {
2185                                         item_name= "listitem";
2186                                         sgml::closeTag(ofs, command_depth + depth, false, item_name);
2187                                         if (environment_inner[depth] == "varlistentry")
2188                                                 sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
2189                                 }
2190                         }
2191
2192                         if (style->latextype == LATEX_ENVIRONMENT) {
2193                                 if (!style->latexparam().empty()) {
2194                                         if (style->latexparam() == "CDATA")
2195                                                 ofs << "<![CDATA[";
2196                                         else
2197                                                 sgml::openTag(ofs, depth + command_depth, false, style->latexparam());
2198                                 }
2199                                 break;
2200                         }
2201
2202                         desc_on = (style->labeltype == LABEL_MANUAL);
2203
2204                         environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
2205                         sgml::openTag(ofs, depth + 1 + command_depth,
2206                                     false, environment_inner[depth]);
2207
2208                         item_name = desc_on ? "term" : "para";
2209                         sgml::openTag(ofs, depth + 1 + command_depth,
2210                                     false, item_name);
2211                         break;
2212                 default:
2213                         sgml::openTag(ofs, depth + command_depth,
2214                                     false, style->latexname());
2215                         break;
2216                 }
2217
2218                 simpleDocBookOnePar(ofs, par, desc_on,
2219                                     depth + 1 + command_depth);
2220                 par = par->next();
2221
2222                 string end_tag;
2223                 // write closing SGML tags
2224                 switch (style->latextype) {
2225                 case LATEX_COMMAND:
2226                         end_tag = c_params.empty() ? "title" : c_params;
2227                         sgml::closeTag(ofs, depth + command_depth,
2228                                      false, end_tag);
2229                         break;
2230                 case LATEX_ENVIRONMENT:
2231                         if (!style->latexparam().empty()) {
2232                                 if (style->latexparam() == "CDATA")
2233                                         ofs << "]]>";
2234                                 else
2235                                         sgml::closeTag(ofs, depth + command_depth, false, style->latexparam());
2236                         }
2237                         break;
2238                 case LATEX_ITEM_ENVIRONMENT:
2239                         if (desc_on == 1) break;
2240                         end_tag = "para";
2241                         sgml::closeTag(ofs, depth + 1 + command_depth, false, end_tag);
2242                         break;
2243                 case LATEX_PARAGRAPH:
2244                         sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
2245                         break;
2246                 default:
2247                         sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
2248                         break;
2249                 }
2250         }
2251
2252         // Close open tags
2253         for (int d = depth; d >= 0; --d) {
2254                 if (!environment_stack[depth].empty()) {
2255                         if (environment_inner[depth] != "!-- --") {
2256                                 item_name = "listitem";
2257                                 sgml::closeTag(ofs, command_depth + depth, false, item_name);
2258                                if (environment_inner[depth] == "varlistentry")
2259                                        sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
2260                         }
2261
2262                         sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2263                 }
2264         }
2265
2266         for (int j = command_depth; j >= 0 ; --j)
2267                 if (!command_stack[j].empty()) {
2268                         sgml::closeTag(ofs, j, false, command_stack[j]);
2269                         ofs << endl;
2270                 }
2271
2272         ofs << "\n\n";
2273         sgml::closeTag(ofs, 0, false, top_element);
2274
2275         ofs.close();
2276         // How to check for successful close
2277
2278         // we want this to be true outside previews (for insetexternal)
2279         niceFile = true;
2280 }
2281
2282
2283 void Buffer::simpleDocBookOnePar(ostream & os,
2284                                  Paragraph * par, int & desc_on,
2285                                  Paragraph::depth_type depth) const
2286 {
2287         bool emph_flag = false;
2288
2289         LyXLayout_ptr const & style = par->layout();
2290
2291         LyXFont font_old = (style->labeltype == LABEL_MANUAL ? style->labelfont : style->font);
2292
2293         int char_line_count = depth;
2294         //if (!style.free_spacing)
2295         //      os << string(depth,' ');
2296
2297         // parsing main loop
2298         for (pos_type i = 0; i < par->size(); ++i) {
2299                 LyXFont font = par->getFont(params, i);
2300
2301                 // handle <emphasis> tag
2302                 if (font_old.emph() != font.emph()) {
2303                         if (font.emph() == LyXFont::ON) {
2304                                 if (style->latexparam() == "CDATA")
2305                                         os << "]]>";
2306                                 os << "<emphasis>";
2307                                 if (style->latexparam() == "CDATA")
2308                                         os << "<![CDATA[";
2309                                 emph_flag = true;
2310                         } else if (i) {
2311                                 if (style->latexparam() == "CDATA")
2312                                         os << "]]>";
2313                                 os << "</emphasis>";
2314                                 if (style->latexparam() == "CDATA")
2315                                         os << "<![CDATA[";
2316                                 emph_flag = false;
2317                         }
2318                 }
2319
2320
2321                 if (par->isInset(i)) {
2322                         Inset * inset = par->getInset(i);
2323                         // don't print the inset in position 0 if desc_on == 3 (label)
2324                         if (i || desc_on != 3) {
2325                                 if (style->latexparam() == "CDATA")
2326                                         os << "]]>";
2327                                 inset->docbook(this, os, false);
2328                                 if (style->latexparam() == "CDATA")
2329                                         os << "<![CDATA[";
2330                         }
2331                 } else {
2332                         char c = par->getChar(i);
2333                         bool ws;
2334                         string str;
2335                         boost::tie(ws, str) = sgml::escapeChar(c);
2336
2337                         if (style->pass_thru) {
2338                                 os << c;
2339                         } else if (style->free_spacing || par->isFreeSpacing() || c != ' ') {
2340                                         os << str;
2341                         } else if (desc_on ==1) {
2342                                 ++char_line_count;
2343                                 os << "\n</term><listitem><para>";
2344                                 desc_on = 2;
2345                         } else {
2346                                 os << ' ';
2347                         }
2348                 }
2349                 font_old = font;
2350         }
2351
2352         if (emph_flag) {
2353                 if (style->latexparam() == "CDATA")
2354                         os << "]]>";
2355                 os << "</emphasis>";
2356                 if (style->latexparam() == "CDATA")
2357                         os << "<![CDATA[";
2358         }
2359
2360         // resets description flag correctly
2361         if (desc_on == 1) {
2362                 // <term> not closed...
2363                 os << "</term>\n<listitem><para>&nbsp;</para>";
2364         }
2365         if (style->free_spacing)
2366                 os << '\n';
2367 }
2368
2369
2370 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
2371 // Other flags: -wall -v0 -x
2372 int Buffer::runChktex()
2373 {
2374         if (!users->text) return 0;
2375
2376         users->owner()->busy(true);
2377
2378         // get LaTeX-Filename
2379         string const name = getLatexName();
2380         string path = filePath();
2381
2382         string const org_path = path;
2383         if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
2384                 path = tmppath;
2385         }
2386
2387         Path p(path); // path to LaTeX file
2388         users->owner()->message(_("Running chktex..."));
2389
2390         // Remove all error insets
2391         bool const removedErrorInsets = users->removeAutoInsets();
2392
2393         // Generate the LaTeX file if neccessary
2394         makeLaTeXFile(name, org_path, false);
2395
2396         TeXErrors terr;
2397         Chktex chktex(lyxrc.chktex_command, name, filePath());
2398         int res = chktex.run(terr); // run chktex
2399
2400         if (res == -1) {
2401                 Alert::alert(_("chktex did not work!"),
2402                            _("Could not run with file:"), name);
2403         } else if (res > 0) {
2404                 // Insert all errors as errors boxes
2405                 users->insertErrors(terr);
2406         }
2407
2408         // if we removed error insets before we ran chktex or if we inserted
2409         // error insets after we ran chktex, this must be run:
2410         if (removedErrorInsets || res) {
2411 #warning repaint needed here, or do you mean update() ?
2412                 users->repaint();
2413                 users->fitCursor();
2414         }
2415         users->owner()->busy(false);
2416
2417         return res;
2418 }
2419
2420
2421 void Buffer::validate(LaTeXFeatures & features) const
2422 {
2423         LyXTextClass const & tclass = params.getLyXTextClass();
2424
2425         if (params.tracking_changes) {
2426                 features.require("dvipost");
2427                 features.require("color");
2428         }
2429
2430         // AMS Style is at document level
2431         if (params.use_amsmath == BufferParams::AMS_ON
2432             || tclass.provides(LyXTextClass::amsmath))
2433                 features.require("amsmath");
2434
2435         for_each(paragraphs.begin(), paragraphs.end(),
2436                  boost::bind(&Paragraph::validate, _1, boost::ref(features)));
2437
2438         // the bullet shapes are buffer level not paragraph level
2439         // so they are tested here
2440         for (int i = 0; i < 4; ++i) {
2441                 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
2442                         int const font = params.user_defined_bullets[i].getFont();
2443                         if (font == 0) {
2444                                 int const c = params
2445                                         .user_defined_bullets[i]
2446                                         .getCharacter();
2447                                 if (c == 16
2448                                    || c == 17
2449                                    || c == 25
2450                                    || c == 26
2451                                    || c == 31) {
2452                                         features.require("latexsym");
2453                                 }
2454                         } else if (font == 1) {
2455                                 features.require("amssymb");
2456                         } else if ((font >= 2 && font <= 5)) {
2457                                 features.require("pifont");
2458                         }
2459                 }
2460         }
2461
2462         if (lyxerr.debugging(Debug::LATEX)) {
2463                 features.showStruct();
2464         }
2465 }
2466
2467
2468 vector<string> const Buffer::getLabelList() const
2469 {
2470         /// if this is a child document and the parent is already loaded
2471         /// Use the parent's list instead  [ale990407]
2472         if (!params.parentname.empty()
2473             && bufferlist.exists(params.parentname)) {
2474                 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
2475                 if (tmp)
2476                         return tmp->getLabelList();
2477         }
2478
2479         vector<string> label_list;
2480         for (inset_iterator it = inset_const_iterator_begin();
2481              it != inset_const_iterator_end(); ++it) {
2482                 vector<string> const l = it->getLabelList();
2483                 label_list.insert(label_list.end(), l.begin(), l.end());
2484         }
2485         return label_list;
2486 }
2487
2488
2489 // This is also a buffer property (ale)
2490 void Buffer::fillWithBibKeys(vector<pair<string, string> > & keys) const
2491 {
2492         /// if this is a child document and the parent is already loaded
2493         /// use the parent's list instead  [ale990412]
2494         if (!params.parentname.empty() && bufferlist.exists(params.parentname)) {
2495                 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
2496                 if (tmp) {
2497                         tmp->fillWithBibKeys(keys);
2498                         return;
2499                 }
2500         }
2501
2502         for (inset_iterator it = inset_const_iterator_begin();
2503                 it != inset_const_iterator_end(); ++it) {
2504                 if (it->lyxCode() == Inset::BIBTEX_CODE)
2505                         static_cast<InsetBibtex &>(*it).fillWithBibKeys(this, keys);
2506                 else if (it->lyxCode() == Inset::INCLUDE_CODE)
2507                         static_cast<InsetInclude &>(*it).fillWithBibKeys(keys);
2508                 else if (it->lyxCode() == Inset::BIBITEM_CODE) {
2509                         InsetBibitem & bib = static_cast<InsetBibitem &>(*it);
2510                         string const key = bib.getContents();
2511                         string const opt = bib.getOptions();
2512                         string const ref; // = pit->asString(this, false);
2513                         string const info = opt + "TheBibliographyRef" + ref;
2514                         keys.push_back(pair<string, string>(key, info));
2515                 }
2516         }
2517 }
2518
2519
2520 bool Buffer::isDepClean(string const & name) const
2521 {
2522         DepClean::const_iterator it = dep_clean_.find(name);
2523         if (it == dep_clean_.end())
2524                 return true;
2525         return it->second;
2526 }
2527
2528
2529 void Buffer::markDepClean(string const & name)
2530 {
2531         dep_clean_[name] = true;
2532 }
2533
2534
2535 bool Buffer::dispatch(string const & command, bool * result)
2536 {
2537         // Split command string into command and argument
2538         string cmd;
2539         string line = ltrim(command);
2540         string const arg = trim(split(line, cmd, ' '));
2541
2542         return dispatch(lyxaction.LookupFunc(cmd), arg, result);
2543 }
2544
2545
2546 bool Buffer::dispatch(int action, string const & argument, bool * result)
2547 {
2548         bool dispatched = true;
2549
2550         switch (action) {
2551                 case LFUN_EXPORT: {
2552                         bool const tmp = Exporter::Export(this, argument, false);
2553                         if (result)
2554                                 *result = tmp;
2555                         break;
2556                 }
2557
2558                 default:
2559                         dispatched = false;
2560         }
2561         return dispatched;
2562 }
2563
2564
2565 void Buffer::resizeInsets(BufferView * bv)
2566 {
2567         /// then remove all LyXText in text-insets
2568         for_each(paragraphs.begin(), paragraphs.end(),
2569                  boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
2570 }
2571
2572
2573 void Buffer::redraw()
2574 {
2575 #warning repaint needed here, or do you mean update() ?
2576         users->repaint();
2577         users->fitCursor();
2578 }
2579
2580
2581 void Buffer::changeLanguage(Language const * from, Language const * to)
2582 {
2583
2584         ParIterator end = par_iterator_end();
2585         for (ParIterator it = par_iterator_begin(); it != end; ++it)
2586                 (*it)->changeLanguage(params, from, to);
2587 }
2588
2589
2590 bool Buffer::isMultiLingual()
2591 {
2592         ParIterator end = par_iterator_end();
2593         for (ParIterator it = par_iterator_begin(); it != end; ++it)
2594                 if ((*it)->isMultiLingual(params))
2595                         return true;
2596
2597         return false;
2598 }
2599
2600
2601 void Buffer::inset_iterator::setParagraph()
2602 {
2603         while (pit != pend) {
2604                 it = pit->insetlist.begin();
2605                 if (it != pit->insetlist.end())
2606                         return;
2607                 ++pit;
2608         }
2609 }
2610
2611
2612 Inset * Buffer::getInsetFromID(int id_arg) const
2613 {
2614         for (inset_iterator it = inset_const_iterator_begin();
2615                  it != inset_const_iterator_end(); ++it)
2616         {
2617                 if (it->id() == id_arg)
2618                         return &(*it);
2619                 Inset * in = it->getInsetFromID(id_arg);
2620                 if (in)
2621                         return in;
2622         }
2623         return 0;
2624 }
2625
2626
2627 Paragraph * Buffer::getParFromID(int id) const
2628 {
2629         if (id < 0)
2630                 return 0;
2631
2632         // why should we allow < 0 ??
2633         //lyx::Assert(id >= 0);
2634
2635         ParConstIterator it(par_iterator_begin());
2636         ParConstIterator end(par_iterator_end());
2637
2638         for (; it != end; ++it) {
2639                 // go on then, show me how to remove
2640                 // the cast
2641                 if ((*it)->id() == id) {
2642                         return const_cast<Paragraph*>(*it);
2643                 }
2644         }
2645
2646         return 0;
2647 }
2648
2649
2650 ParIterator Buffer::par_iterator_begin()
2651 {
2652         return ParIterator(&*(paragraphs.begin()));
2653 }
2654
2655
2656 ParIterator Buffer::par_iterator_end()
2657 {
2658         return ParIterator();
2659 }
2660
2661 ParConstIterator Buffer::par_iterator_begin() const
2662 {
2663         return ParConstIterator(&*(paragraphs.begin()));
2664 }
2665
2666
2667 ParConstIterator Buffer::par_iterator_end() const
2668 {
2669         return ParConstIterator();
2670 }
2671
2672
2673
2674 void Buffer::addUser(BufferView * u)
2675 {
2676         users = u;
2677 }
2678
2679
2680 void Buffer::delUser(BufferView *)
2681 {
2682         users = 0;
2683 }
2684
2685
2686 Language const * Buffer::getLanguage() const
2687 {
2688         return params.language;
2689 }
2690
2691
2692 bool Buffer::isClean() const
2693 {
2694         return lyx_clean;
2695 }
2696
2697
2698 bool Buffer::isBakClean() const
2699 {
2700         return bak_clean;
2701 }
2702
2703
2704 void Buffer::markClean() const
2705 {
2706         if (!lyx_clean) {
2707                 lyx_clean = true;
2708                 updateTitles();
2709         }
2710         // if the .lyx file has been saved, we don't need an
2711         // autosave
2712         bak_clean = true;
2713 }
2714
2715
2716 void Buffer::markBakClean()
2717 {
2718         bak_clean = true;
2719 }
2720
2721
2722 void Buffer::setUnnamed(bool flag)
2723 {
2724         unnamed = flag;
2725 }
2726
2727
2728 bool Buffer::isUnnamed()
2729 {
2730         return unnamed;
2731 }
2732
2733
2734 void Buffer::markDirty()
2735 {
2736         if (lyx_clean) {
2737                 lyx_clean = false;
2738                 updateTitles();
2739         }
2740         bak_clean = false;
2741
2742         DepClean::iterator it = dep_clean_.begin();
2743         DepClean::const_iterator const end = dep_clean_.end();
2744
2745         for (; it != end; ++it) {
2746                 it->second = false;
2747         }
2748 }
2749
2750
2751 string const & Buffer::fileName() const
2752 {
2753         return filename_;
2754 }
2755
2756
2757 string const & Buffer::filePath() const
2758 {
2759         return filepath_;
2760 }
2761
2762
2763 bool Buffer::isReadonly() const
2764 {
2765         return read_only;
2766 }
2767
2768
2769 BufferView * Buffer::getUser() const
2770 {
2771         return users;
2772 }
2773
2774
2775 void Buffer::setParentName(string const & name)
2776 {
2777         params.parentname = name;
2778 }
2779
2780
2781 Buffer::inset_iterator::inset_iterator()
2782         : pit(0), pend(0)
2783 {}
2784
2785
2786 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
2787         : pit(p), pend(e)
2788 {
2789         setParagraph();
2790 }
2791
2792
2793 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
2794 {
2795         if (pit != pend) {
2796                 ++it;
2797                 if (it == pit->insetlist.end()) {
2798                         ++pit;
2799                         setParagraph();
2800                 }
2801         }
2802         return *this;
2803 }
2804
2805
2806 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
2807 {
2808         inset_iterator tmp = *this;
2809         ++*this;
2810         return tmp;
2811 }
2812
2813
2814 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
2815 {
2816         return *it.getInset();
2817 }
2818
2819
2820 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
2821 {
2822         return it.getInset();
2823 }
2824
2825
2826 Paragraph * Buffer::inset_iterator::getPar()
2827 {
2828         return &(*pit);
2829 }
2830
2831
2832 lyx::pos_type Buffer::inset_iterator::getPos() const
2833 {
2834         return it.getPos();
2835 }
2836
2837
2838 bool operator==(Buffer::inset_iterator const & iter1,
2839                 Buffer::inset_iterator const & iter2)
2840 {
2841         return iter1.pit == iter2.pit
2842                 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
2843 }
2844
2845
2846 bool operator!=(Buffer::inset_iterator const & iter1,
2847                 Buffer::inset_iterator const & iter2)
2848 {
2849         return !(iter1 == iter2);
2850 }