]> git.lyx.org Git - lyx.git/blob - src/buffer.C
Fix natbib bug spotted by JMarc.
[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 #ifdef __GNUG__
18 #pragma implementation
19 #endif
20
21 #include "buffer.h"
22 #include "bufferlist.h"
23 #include "LyXAction.h"
24 #include "lyxrc.h"
25 #include "lyxlex.h"
26 #include "tex-strings.h"
27 #include "layout.h"
28 #include "bufferview_funcs.h"
29 #include "lyxfont.h"
30 #include "version.h"
31 #include "LaTeX.h"
32 #include "Chktex.h"
33 #include "debug.h"
34 #include "LaTeXFeatures.h"
35 #include "lyxtext.h"
36 #include "gettext.h"
37 #include "language.h"
38 #include "encoding.h"
39 #include "exporter.h"
40 #include "Lsstream.h"
41 #include "converter.h"
42 #include "BufferView.h"
43 #include "ParagraphParameters.h"
44 #include "iterators.h"
45 #include "lyxtextclasslist.h"
46 #include "sgml.h"
47 #include "paragraph_funcs.h"
48 #include "author.h"
49
50 #include "frontends/LyXView.h"
51
52 #include "mathed/formulamacro.h"
53 #include "mathed/formula.h"
54
55 #include "insets/inset.h"
56 #include "insets/inseterror.h"
57 #include "insets/insetlabel.h"
58 #include "insets/insetref.h"
59 #include "insets/inseturl.h"
60 #include "insets/insetnote.h"
61 #include "insets/insetquotes.h"
62 #include "insets/insetlatexaccent.h"
63 #include "insets/insetbib.h"
64 #include "insets/insetcite.h"
65 #include "insets/insetexternal.h"
66 #include "insets/insetindex.h"
67 #include "insets/insetinclude.h"
68 #include "insets/insettoc.h"
69 #include "insets/insetparent.h"
70 #include "insets/insetspecialchar.h"
71 #include "insets/insettext.h"
72 #include "insets/insetert.h"
73 #include "insets/insetgraphics.h"
74 #include "insets/insetfoot.h"
75 #include "insets/insetmarginal.h"
76 #include "insets/insetoptarg.h"
77 #include "insets/insetminipage.h"
78 #include "insets/insetfloat.h"
79 #include "insets/insetwrap.h"
80 #include "insets/insettabular.h"
81 #if 0
82 #include "insets/insettheorem.h"
83 #include "insets/insetlist.h"
84 #endif
85 #include "insets/insetcaption.h"
86 #include "insets/insetfloatlist.h"
87
88 #include "frontends/Dialogs.h"
89 #include "frontends/Alert.h"
90
91 #include "graphics/Previews.h"
92
93 #include "support/textutils.h"
94 #include "support/filetools.h"
95 #include "support/path.h"
96 #include "support/os.h"
97 #include "support/lyxlib.h"
98 #include "support/FileInfo.h"
99 #include "support/lyxmanip.h"
100 #include "support/lyxalgo.h" // for lyx::count
101 #include "support/lyxtime.h"
102
103 #include <boost/bind.hpp>
104 #include <boost/tuple/tuple.hpp>
105 #include "BoostFormat.h"
106
107 #include <fstream>
108 #include <iomanip>
109 #include <map>
110 #include <stack>
111 #include <list>
112 #include <algorithm>
113
114 #include <cstdlib>
115 #include <cmath>
116 #include <unistd.h>
117 #include <sys/types.h>
118 #include <utime.h>
119
120 #ifdef HAVE_LOCALE
121 #include <locale>
122 #endif
123
124 #ifndef CXX_GLOBAL_CSTD
125 using std::pow;
126 #endif
127
128 using std::ostream;
129 using std::ofstream;
130 using std::ifstream;
131 using std::fstream;
132 using std::ios;
133 using std::setw;
134 using std::endl;
135 using std::pair;
136 using std::make_pair;
137 using std::vector;
138 using std::map;
139 using std::max;
140 using std::set;
141 using std::stack;
142 using std::list;
143 using std::for_each;
144
145 using lyx::pos_type;
146 using lyx::textclass_type;
147
148 // all these externs should eventually be removed.
149 extern BufferList bufferlist;
150
151 namespace {
152
153 const int LYX_FORMAT = 222;
154
155 } // namespace anon
156
157 Buffer::Buffer(string const & file, bool ronly)
158         : niceFile(true), lyx_clean(true), bak_clean(true),
159           unnamed(false), read_only(ronly),
160           filename_(file), users(0)
161 {
162         lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
163         filepath_ = OnlyPath(file);
164         lyxvc.buffer(this);
165         if (read_only || lyxrc.use_tempdir) {
166                 tmppath = CreateBufferTmpDir();
167         } else {
168                 tmppath.erase();
169         }
170
171         // set initial author
172         authorlist.record(Author(lyxrc.user_name, lyxrc.user_email)); 
173 }
174
175
176 Buffer::~Buffer()
177 {
178         lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
179         // here the buffer should take care that it is
180         // saved properly, before it goes into the void.
181
182         // make sure that views using this buffer
183         // forgets it.
184         if (users)
185                 users->buffer(0);
186
187         if (!tmppath.empty()) {
188                 DestroyBufferTmpDir(tmppath);
189         }
190
191         paragraphs.clear();
192
193         // Remove any previewed LaTeX snippets assocoated with this buffer.
194         grfx::Previews::get().removeLoader(this);
195 }
196
197
198 string const Buffer::getLatexName(bool no_path) const
199 {
200         string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
201         if (no_path)
202                 return OnlyFilename(name);
203         else
204                 return name;
205 }
206
207
208 pair<Buffer::LogType, string> const Buffer::getLogName() const
209 {
210         string const filename = getLatexName(false);
211
212         if (filename.empty())
213                 return make_pair(Buffer::latexlog, string());
214
215         string path = OnlyPath(filename);
216
217         if (lyxrc.use_tempdir || !IsDirWriteable(path))
218                 path = tmppath;
219
220         string const fname = AddName(path,
221                                      OnlyFilename(ChangeExtension(filename,
222                                                                   ".log")));
223         string const bname =
224                 AddName(path, OnlyFilename(
225                         ChangeExtension(filename,
226                                         formats.extension("literate") + ".out")));
227
228         // If no Latex log or Build log is newer, show Build log
229
230         FileInfo const f_fi(fname);
231         FileInfo const b_fi(bname);
232
233         if (b_fi.exist() &&
234             (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
235                 lyxerr[Debug::FILES] << "Log name calculated as: " << bname << endl;
236                 return make_pair(Buffer::buildlog, bname);
237         }
238         lyxerr[Debug::FILES] << "Log name calculated as: " << fname << endl;
239         return make_pair(Buffer::latexlog, fname);
240 }
241
242
243 void Buffer::setReadonly(bool flag)
244 {
245         if (read_only != flag) {
246                 read_only = flag;
247                 updateTitles();
248                 users->owner()->getDialogs().updateBufferDependent(false);
249         }
250 }
251
252
253 AuthorList & Buffer::authors()
254 {
255         return authorlist;
256 }
257
258
259 /// Update window titles of all users
260 // Should work on a list
261 void Buffer::updateTitles() const
262 {
263         if (users)
264                 users->owner()->updateWindowTitle();
265 }
266
267
268 /// Reset autosave timer of all users
269 // Should work on a list
270 void Buffer::resetAutosaveTimers() const
271 {
272         if (users)
273                 users->owner()->resetAutosaveTimer();
274 }
275
276
277 void Buffer::setFileName(string const & newfile)
278 {
279         filename_ = MakeAbsPath(newfile);
280         filepath_ = OnlyPath(filename_);
281         setReadonly(IsFileWriteable(filename_) == 0);
282         updateTitles();
283 }
284
285
286 // We'll remove this later. (Lgb)
287 namespace {
288
289 string last_inset_read;
290
291 #ifdef WITH_WARNINGS
292 #warning And _why_ is this here? (Lgb)
293 #endif
294 int unknown_layouts;
295 int unknown_tokens;
296 vector<int> author_ids;
297
298 } // anon
299
300
301 // candidate for move to BufferView
302 // (at least some parts in the beginning of the func)
303 //
304 // Uwe C. Schroeder
305 // changed to be public and have one parameter
306 // if par = 0 normal behavior
307 // else insert behavior
308 // Returns false if "\the_end" is not read (Asger)
309 bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
310 {
311         unknown_layouts = 0;
312         unknown_tokens = 0;
313         author_ids.clear();
314
315         int pos = 0;
316         Paragraph::depth_type depth = 0;
317         bool the_end_read = false;
318
319         Paragraph * first_par = 0;
320         LyXFont font(LyXFont::ALL_INHERIT, params.language);
321
322 #if 0
323         if (file_format < 216 && params.language->lang() == "hebrew")
324                 font.setLanguage(default_language);
325 #endif
326
327         if (!par) {
328                 par = new Paragraph;
329                 par->layout(params.getLyXTextClass().defaultLayout());
330         } else {
331                 // We are inserting into an existing document
332                 users->text->breakParagraph(users);
333                 first_par = users->text->ownerParagraph();
334                 pos = 0;
335                 markDirty();
336                 // We don't want to adopt the parameters from the
337                 // document we insert, so we skip until the text begins:
338                 while (lex.isOK()) {
339                         lex.nextToken();
340                         string const pretoken = lex.getString();
341                         if (pretoken == "\\layout") {
342                                 lex.pushToken(pretoken);
343                                 break;
344                         }
345                 }
346         }
347
348         while (lex.isOK()) {
349                 lex.nextToken();
350                 string const token = lex.getString();
351
352                 if (token.empty()) continue;
353
354                 lyxerr[Debug::PARSER] << "Handling token: `"
355                                       << token << '\'' << endl;
356
357                 the_end_read =
358                         parseSingleLyXformat2Token(lex, par, first_par,
359                                                    token, pos, depth,
360                                                    font);
361         }
362
363         if (!first_par)
364                 first_par = par;
365
366         paragraphs.set(first_par);
367
368         if (unknown_layouts > 0) {
369                 string s = _("Couldn't set the layout for ");
370                 if (unknown_layouts == 1) {
371                         s += _("one paragraph");
372                 } else {
373                         s += tostr(unknown_layouts);
374                         s += _(" paragraphs");
375                 }
376 #if USE_BOOST_FORMAT
377                 Alert::alert(_("Textclass Loading Error!"), s,
378                            boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
379 #else
380                 Alert::alert(_("Textclass Loading Error!"), s,
381                              _("When reading ") + fileName());
382 #endif
383         }
384
385         if (unknown_tokens > 0) {
386                 string s = _("Encountered ");
387                 if (unknown_tokens == 1) {
388                         s += _("one unknown token");
389                 } else {
390                         s += tostr(unknown_tokens);
391                         s += _(" unknown tokens");
392                 }
393 #if USE_BOOST_FORMAT
394                 Alert::alert(_("Textclass Loading Error!"), s,
395                            boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
396 #else
397                 Alert::alert(_("Textclass Loading Error!"), s,
398                              _("When reading ") +  fileName());
399 #endif
400         }
401
402         return the_end_read;
403 }
404
405
406 namespace {
407         // This stuff is, in the traditional LyX terminology, Super UGLY
408         // but this code is too b0rken to admit of a better solution yet
409         Change current_change;
410 };
411  
412
413 bool
414 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
415                                    Paragraph *& first_par,
416                                    string const & token, int & pos,
417                                    Paragraph::depth_type & depth,
418                                    LyXFont & font
419         )
420 {
421         bool the_end_read = false;
422
423         // The order of the tags tested may seem unnatural, but this
424         // has been done in order to reduce the number of string
425         // comparisons needed to recognize a given token. This leads
426         // on large documents like UserGuide to a reduction of a
427         // factor 5! (JMarc)
428         if (token[0] != '\\') {
429                 for (string::const_iterator cit = token.begin();
430                      cit != token.end(); ++cit) {
431                         par->insertChar(pos, (*cit), font, current_change);
432                         ++pos;
433                 }
434         } else if (token == "\\layout") {
435                 // reset the font as we start a new layout and if the font is
436                 // not ALL_INHERIT,document_language then it will be set to the
437                 // right values after this tag (Jug 20020420)
438                 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
439
440 #if 0
441                 if (file_format < 216 && params.language->lang() == "hebrew")
442                         font.setLanguage(default_language);
443 #endif
444
445                 lex.eatLine();
446                 string layoutname = lex.getString();
447
448                 LyXTextClass const & tclass = params.getLyXTextClass();
449
450                 if (layoutname.empty()) {
451                         layoutname = tclass.defaultLayoutName();
452                 }
453                 bool hasLayout = tclass.hasLayout(layoutname);
454                 if (!hasLayout) {
455                         lyxerr << "Layout '" << layoutname << "' does not"
456                                << " exist in textclass '" << tclass.name()
457                                << "'." << endl;
458                         lyxerr << "Trying to use default layout instead."
459                                << endl;
460                         layoutname = tclass.defaultLayoutName();
461                 }
462
463 #ifdef USE_CAPTION
464                 // The is the compability reading of layout caption.
465                 // It can be removed in LyX version 1.3.0. (Lgb)
466                 if (compare_ascii_no_case(layoutname, "caption") == 0) {
467                         // We expect that the par we are now working on is
468                         // really inside a InsetText inside a InsetFloat.
469                         // We also know that captions can only be
470                         // one paragraph. (Lgb)
471
472                         // We should now read until the next "\layout"
473                         // is reached.
474                         // This is probably not good enough, what if the
475                         // caption is the last par in the document (Lgb)
476                         istream & ist = lex.getStream();
477                         stringstream ss;
478                         string line;
479                         int begin = 0;
480                         while (true) {
481                                 getline(ist, line);
482                                 if (prefixIs(line, "\\layout")) {
483                                         lex.pushToken(line);
484                                         break;
485                                 }
486                                 if (prefixIs(line, "\\begin_inset"))
487                                         ++begin;
488                                 if (prefixIs(line, "\\end_inset")) {
489                                         if (begin)
490                                                 --begin;
491                                         else {
492                                                 lex.pushToken(line);
493                                                 break;
494                                         }
495                                 }
496
497                                 ss << line << '\n';
498                         }
499                         // Now we should have the whole layout in ss
500                         // we should now be able to give this to the
501                         // caption inset.
502                         ss << "\\end_inset\n";
503
504                         // This seems like a bug in stringstream.
505                         // We really should be able to use ss
506                         // directly. (Lgb)
507                         istringstream is(ss.str());
508                         LyXLex tmplex(0, 0);
509                         tmplex.setStream(is);
510                         Inset * inset = new InsetCaption;
511                         inset->Read(this, tmplex);
512                         par->InsertInset(pos, inset, font);
513                         ++pos;
514                 } else {
515 #endif
516                         if (!first_par)
517                                 first_par = par;
518                         else {
519                                 par = new Paragraph(par);
520                                 par->layout(params.getLyXTextClass().defaultLayout());
521                                 if (params.tracking_changes)
522                                         par->trackChanges();
523                         }
524                         pos = 0;
525                         par->layout(params.getLyXTextClass()[layoutname]);
526                         // Test whether the layout is obsolete.
527                         LyXLayout_ptr const & layout = par->layout();
528                         if (!layout->obsoleted_by().empty())
529                                 par->layout(params.getLyXTextClass()[layout->obsoleted_by()]);
530                         par->params().depth(depth);
531 #if USE_CAPTION
532                 }
533 #endif
534
535         } else if (token == "\\end_inset") {
536                 lyxerr << "Solitary \\end_inset. Missing \\begin_inset?.\n"
537                        << "Last inset read was: " << last_inset_read
538                        << endl;
539                 // Simply ignore this. The insets do not have
540                 // to read this.
541                 // But insets should read it, it is a part of
542                 // the inset isn't it? Lgb.
543         } else if (token == "\\begin_inset") {
544                 readInset(lex, par, pos, font);
545         } else if (token == "\\family") {
546                 lex.next();
547                 font.setLyXFamily(lex.getString());
548         } else if (token == "\\series") {
549                 lex.next();
550                 font.setLyXSeries(lex.getString());
551         } else if (token == "\\shape") {
552                 lex.next();
553                 font.setLyXShape(lex.getString());
554         } else if (token == "\\size") {
555                 lex.next();
556                 font.setLyXSize(lex.getString());
557         } else if (token == "\\lang") {
558                 lex.next();
559                 string const tok = lex.getString();
560                 Language const * lang = languages.getLanguage(tok);
561                 if (lang) {
562                         font.setLanguage(lang);
563                 } else {
564                         font.setLanguage(params.language);
565                         lex.printError("Unknown language `$$Token'");
566                 }
567         } else if (token == "\\numeric") {
568                 lex.next();
569                 font.setNumber(font.setLyXMisc(lex.getString()));
570         } else if (token == "\\emph") {
571                 lex.next();
572                 font.setEmph(font.setLyXMisc(lex.getString()));
573         } else if (token == "\\bar") {
574                 lex.next();
575                 string const tok = lex.getString();
576                 // This is dirty, but gone with LyX3. (Asger)
577                 if (tok == "under")
578                         font.setUnderbar(LyXFont::ON);
579                 else if (tok == "no")
580                         font.setUnderbar(LyXFont::OFF);
581                 else if (tok == "default")
582                         font.setUnderbar(LyXFont::INHERIT);
583                 else
584                         lex.printError("Unknown bar font flag "
585                                        "`$$Token'");
586         } else if (token == "\\noun") {
587                 lex.next();
588                 font.setNoun(font.setLyXMisc(lex.getString()));
589         } else if (token == "\\color") {
590                 lex.next();
591                 font.setLyXColor(lex.getString());
592         } else if (token == "\\SpecialChar") {
593                 LyXLayout_ptr const & layout = par->layout();
594
595                 // Insets don't make sense in a free-spacing context! ---Kayvan
596                 if (layout->free_spacing || par->isFreeSpacing()) {
597                         if (lex.isOK()) {
598                                 lex.next();
599                                 string const next_token = lex.getString();
600                                 if (next_token == "\\-") {
601                                         par->insertChar(pos, '-', font, current_change);
602                                 } else if (next_token == "~") {
603                                         par->insertChar(pos, ' ', font, current_change);
604                                 } else {
605                                         lex.printError("Token `$$Token' "
606                                                        "is in free space "
607                                                        "paragraph layout!");
608                                         --pos;
609                                 }
610                         }
611                 } else {
612                         Inset * inset = new InsetSpecialChar;
613                         inset->read(this, lex);
614                         par->insertInset(pos, inset, font, current_change);
615                 }
616                 ++pos;
617         } else if (token == "\\i") {
618                 Inset * inset = new InsetLatexAccent;
619                 inset->read(this, lex);
620                 par->insertInset(pos, inset, font, current_change);
621                 ++pos;
622         } else if (token == "\\backslash") {
623                 par->insertChar(pos, '\\', font, current_change);
624                 ++pos;
625         } else if (token == "\\begin_deeper") {
626                 ++depth;
627         } else if (token == "\\end_deeper") {
628                 if (!depth) {
629                         lex.printError("\\end_deeper: "
630                                        "depth is already null");
631                 }
632                 else
633                         --depth;
634         } else if (token == "\\begin_preamble") {
635                 params.readPreamble(lex);
636         } else if (token == "\\textclass") {
637                 lex.eatLine();
638                 pair<bool, textclass_type> pp =
639                         textclasslist.NumberOfClass(lex.getString());
640                 if (pp.first) {
641                         params.textclass = pp.second;
642                 } else {
643 #if USE_BOOST_FORMAT
644                         Alert::alert(_("Textclass error"),
645                                 boost::io::str(boost::format(_("The document uses an unknown textclass \"%1$s\".")) % lex.getString()),
646                                 _("-- substituting default."));
647 #else
648                         Alert::alert(
649                                 _("Textclass error"),
650                                 _("The document uses an unknown textclass ")
651                                 + lex.getString(),
652                                 _("-- substituting default."));
653 #endif
654                         params.textclass = 0;
655                 }
656                 if (!params.getLyXTextClass().load()) {
657                         // if the textclass wasn't loaded properly
658                         // we need to either substitute another
659                         // or stop loading the file.
660                         // I can substitute but I don't see how I can
661                         // stop loading... ideas??  ARRae980418
662 #if USE_BOOST_FORMAT
663                         Alert::alert(_("Textclass Loading Error!"),
664                                    boost::io::str(boost::format(_("Can't load textclass %1$s")) %
665                                    params.getLyXTextClass().name()),
666                                    _("-- substituting default."));
667 #else
668                         Alert::alert(_("Textclass Loading Error!"),
669                                      _("Can't load textclass ")
670                                      + params.getLyXTextClass().name(),
671                                      _("-- substituting default."));
672 #endif
673                         params.textclass = 0;
674                 }
675         } else if (token == "\\options") {
676                 lex.eatLine();
677                 params.options = lex.getString();
678         } else if (token == "\\language") {
679                 params.readLanguage(lex);
680         } else if (token == "\\fontencoding") {
681                 lex.eatLine();
682         } else if (token == "\\inputencoding") {
683                 lex.eatLine();
684                 params.inputenc = lex.getString();
685         } else if (token == "\\graphics") {
686                 params.readGraphicsDriver(lex);
687         } else if (token == "\\fontscheme") {
688                 lex.eatLine();
689                 params.fonts = lex.getString();
690         } else if (token == "\\noindent") {
691                 par->params().noindent(true);
692         } else if (token == "\\leftindent") {
693                 lex.nextToken();
694                 LyXLength value(lex.getString());
695                 par->params().leftIndent(value);
696         } else if (token == "\\fill_top") {
697                 par->params().spaceTop(VSpace(VSpace::VFILL));
698         } else if (token == "\\fill_bottom") {
699                 par->params().spaceBottom(VSpace(VSpace::VFILL));
700         } else if (token == "\\line_top") {
701                 par->params().lineTop(true);
702         } else if (token == "\\line_bottom") {
703                 par->params().lineBottom(true);
704         } else if (token == "\\pagebreak_top") {
705                 par->params().pagebreakTop(true);
706         } else if (token == "\\pagebreak_bottom") {
707                 par->params().pagebreakBottom(true);
708         } else if (token == "\\start_of_appendix") {
709                 par->params().startOfAppendix(true);
710         } else if (token == "\\paragraph_separation") {
711                 int tmpret = lex.findToken(string_paragraph_separation);
712                 if (tmpret == -1)
713                         ++tmpret;
714                 params.paragraph_separation =
715                         static_cast<BufferParams::PARSEP>(tmpret);
716         } else if (token == "\\defskip") {
717                 lex.nextToken();
718                 params.defskip = VSpace(lex.getString());
719         } else if (token == "\\quotes_language") {
720                 int tmpret = lex.findToken(string_quotes_language);
721                 if (tmpret == -1)
722                         ++tmpret;
723                 InsetQuotes::quote_language tmpl =
724                         InsetQuotes::EnglishQ;
725                 switch (tmpret) {
726                 case 0:
727                         tmpl = InsetQuotes::EnglishQ;
728                         break;
729                 case 1:
730                         tmpl = InsetQuotes::SwedishQ;
731                         break;
732                 case 2:
733                         tmpl = InsetQuotes::GermanQ;
734                         break;
735                 case 3:
736                         tmpl = InsetQuotes::PolishQ;
737                         break;
738                 case 4:
739                         tmpl = InsetQuotes::FrenchQ;
740                         break;
741                 case 5:
742                         tmpl = InsetQuotes::DanishQ;
743                         break;
744                 }
745                 params.quotes_language = tmpl;
746         } else if (token == "\\quotes_times") {
747                 lex.nextToken();
748                 switch (lex.getInteger()) {
749                 case 1:
750                         params.quotes_times = InsetQuotes::SingleQ;
751                         break;
752                 case 2:
753                         params.quotes_times = InsetQuotes::DoubleQ;
754                         break;
755                 }
756         } else if (token == "\\papersize") {
757                 int tmpret = lex.findToken(string_papersize);
758                 if (tmpret == -1)
759                         ++tmpret;
760                 else
761                         params.papersize2 = tmpret;
762         } else if (token == "\\paperpackage") {
763                 int tmpret = lex.findToken(string_paperpackages);
764                 if (tmpret == -1) {
765                         ++tmpret;
766                         params.paperpackage = BufferParams::PACKAGE_NONE;
767                 } else
768                         params.paperpackage = tmpret;
769         } else if (token == "\\use_geometry") {
770                 lex.nextToken();
771                 params.use_geometry = lex.getInteger();
772         } else if (token == "\\use_amsmath") {
773                 lex.nextToken();
774                 params.use_amsmath = lex.getInteger();
775         } else if (token == "\\use_natbib") {
776                 lex.nextToken();
777                 params.use_natbib = lex.getInteger();
778         } else if (token == "\\use_numerical_citations") {
779                 lex.nextToken();
780                 params.use_numerical_citations = lex.getInteger();
781         } else if (token == "\\tracking_changes") {
782                 lex.nextToken();
783                 params.tracking_changes = lex.getInteger();
784                 // mark the first paragraph
785                 if (params.tracking_changes)
786                         par->trackChanges();
787         } else if (token == "\\author") {
788                 lex.nextToken();
789                 istringstream ss(lex.getString());
790                 Author a;
791                 ss >> a;
792                 int aid(authorlist.record(a)); 
793                 lyxerr << "aid is " << aid << endl;
794                 lyxerr << "listed aid is " << author_ids.size() << endl;
795                 author_ids.push_back(authorlist.record(a));
796         } else if (token == "\\paperorientation") {
797                 int tmpret = lex.findToken(string_orientation);
798                 if (tmpret == -1)
799                         ++tmpret;
800                 params.orientation =
801                         static_cast<BufferParams::PAPER_ORIENTATION>(tmpret);
802         } else if (token == "\\paperwidth") {
803                 lex.next();
804                 params.paperwidth = lex.getString();
805         } else if (token == "\\paperheight") {
806                 lex.next();
807                 params.paperheight = lex.getString();
808         } else if (token == "\\leftmargin") {
809                 lex.next();
810                 params.leftmargin = lex.getString();
811         } else if (token == "\\topmargin") {
812                 lex.next();
813                 params.topmargin = lex.getString();
814         } else if (token == "\\rightmargin") {
815                 lex.next();
816                 params.rightmargin = lex.getString();
817         } else if (token == "\\bottommargin") {
818                 lex.next();
819                 params.bottommargin = lex.getString();
820         } else if (token == "\\headheight") {
821                 lex.next();
822                 params.headheight = lex.getString();
823         } else if (token == "\\headsep") {
824                 lex.next();
825                 params.headsep = lex.getString();
826         } else if (token == "\\footskip") {
827                 lex.next();
828                 params.footskip = lex.getString();
829         } else if (token == "\\paperfontsize") {
830                 lex.nextToken();
831                 params.fontsize = rtrim(lex.getString());
832         } else if (token == "\\papercolumns") {
833                 lex.nextToken();
834                 params.columns = lex.getInteger();
835         } else if (token == "\\papersides") {
836                 lex.nextToken();
837                 switch (lex.getInteger()) {
838                 default:
839                 case 1: params.sides = LyXTextClass::OneSide; break;
840                 case 2: params.sides = LyXTextClass::TwoSides; break;
841                 }
842         } else if (token == "\\paperpagestyle") {
843                 lex.nextToken();
844                 params.pagestyle = rtrim(lex.getString());
845         } else if (token == "\\bullet") {
846                 lex.nextToken();
847                 int const index = lex.getInteger();
848                 lex.nextToken();
849                 int temp_int = lex.getInteger();
850                 params.user_defined_bullets[index].setFont(temp_int);
851                 params.temp_bullets[index].setFont(temp_int);
852                 lex.nextToken();
853                 temp_int = lex.getInteger();
854                 params.user_defined_bullets[index].setCharacter(temp_int);
855                 params.temp_bullets[index].setCharacter(temp_int);
856                 lex.nextToken();
857                 temp_int = lex.getInteger();
858                 params.user_defined_bullets[index].setSize(temp_int);
859                 params.temp_bullets[index].setSize(temp_int);
860                 lex.nextToken();
861                 string const temp_str = lex.getString();
862                 if (temp_str != "\\end_bullet") {
863                                 // this element isn't really necessary for
864                                 // parsing but is easier for humans
865                                 // to understand bullets. Put it back and
866                                 // set a debug message?
867                         lex.printError("\\end_bullet expected, got" + temp_str);
868                                 //how can I put it back?
869                 }
870         } else if (token == "\\bulletLaTeX") {
871                 // The bullet class should be able to read this.
872                 lex.nextToken();
873                 int const index = lex.getInteger();
874                 lex.next();
875                 string temp_str = lex.getString();
876                 string sum_str;
877                 while (temp_str != "\\end_bullet") {
878                                 // this loop structure is needed when user
879                                 // enters an empty string since the first
880                                 // thing returned will be the \\end_bullet
881                                 // OR
882                                 // if the LaTeX entry has spaces. Each element
883                                 // therefore needs to be read in turn
884                         sum_str += temp_str;
885                         lex.next();
886                         temp_str = lex.getString();
887                 }
888
889                 params.user_defined_bullets[index].setText(sum_str);
890                 params.temp_bullets[index].setText(sum_str);
891         } else if (token == "\\secnumdepth") {
892                 lex.nextToken();
893                 params.secnumdepth = lex.getInteger();
894         } else if (token == "\\tocdepth") {
895                 lex.nextToken();
896                 params.tocdepth = lex.getInteger();
897         } else if (token == "\\spacing") {
898                 lex.next();
899                 string const tmp = rtrim(lex.getString());
900                 Spacing::Space tmp_space = Spacing::Default;
901                 float tmp_val = 0.0;
902                 if (tmp == "single") {
903                         tmp_space = Spacing::Single;
904                 } else if (tmp == "onehalf") {
905                         tmp_space = Spacing::Onehalf;
906                 } else if (tmp == "double") {
907                         tmp_space = Spacing::Double;
908                 } else if (tmp == "other") {
909                         lex.next();
910                         tmp_space = Spacing::Other;
911                         tmp_val = lex.getFloat();
912                 } else {
913                         lex.printError("Unknown spacing token: '$$Token'");
914                 }
915                 // Small hack so that files written with klyx will be
916                 // parsed correctly.
917                 if (first_par) {
918                         par->params().spacing(Spacing(tmp_space, tmp_val));
919                 } else {
920                         params.spacing.set(tmp_space, tmp_val);
921                 }
922         } else if (token == "\\paragraph_spacing") {
923                 lex.next();
924                 string const tmp = rtrim(lex.getString());
925                 if (tmp == "single") {
926                         par->params().spacing(Spacing(Spacing::Single));
927                 } else if (tmp == "onehalf") {
928                         par->params().spacing(Spacing(Spacing::Onehalf));
929                 } else if (tmp == "double") {
930                         par->params().spacing(Spacing(Spacing::Double));
931                 } else if (tmp == "other") {
932                         lex.next();
933                         par->params().spacing(Spacing(Spacing::Other,
934                                          lex.getFloat()));
935                 } else {
936                         lex.printError("Unknown spacing token: '$$Token'");
937                 }
938         } else if (token == "\\float_placement") {
939                 lex.nextToken();
940                 params.float_placement = lex.getString();
941         } else if (token == "\\align") {
942                 int tmpret = lex.findToken(string_align);
943                 if (tmpret == -1) ++tmpret;
944                 int const tmpret2 = int(pow(2.0, tmpret));
945                 par->params().align(LyXAlignment(tmpret2));
946         } else if (token == "\\added_space_top") {
947                 lex.nextToken();
948                 VSpace value = VSpace(lex.getString());
949                 // only add the length when value > 0 or
950                 // with option keep
951                 if ((value.length().len().value() != 0) ||
952                     value.keep() ||
953                     (value.kind() != VSpace::LENGTH))
954                         par->params().spaceTop(value);
955         } else if (token == "\\added_space_bottom") {
956                 lex.nextToken();
957                 VSpace value = VSpace(lex.getString());
958                 // only add the length when value > 0 or
959                 // with option keep
960                 if ((value.length().len().value() != 0) ||
961                    value.keep() ||
962                     (value.kind() != VSpace::LENGTH))
963                         par->params().spaceBottom(value);
964         } else if (token == "\\labelwidthstring") {
965                 lex.eatLine();
966                 par->params().labelWidthString(lex.getString());
967                 // do not delete this token, it is still needed!
968         } else if (token == "\\newline") {
969                 par->insertChar(pos, Paragraph::META_NEWLINE, font, current_change);
970                 ++pos;
971         } else if (token == "\\LyXTable") {
972                 Inset * inset = new InsetTabular(*this);
973                 inset->read(this, lex);
974                 par->insertInset(pos, inset, font, current_change);
975                 ++pos;
976         } else if (token == "\\hfill") {
977                 par->insertChar(pos, Paragraph::META_HFILL, font, current_change);
978                 ++pos;
979         } else if (token == "\\change_unchanged") {
980                 // Hack ! Needed for empty paragraphs :/
981                 if (!pos)
982                         par->cleanChanges();
983                 current_change = Change(Change::UNCHANGED);
984         } else if (token == "\\change_inserted") {
985                 lex.nextToken();
986                 istringstream istr(lex.getString());
987                 int aid;
988                 lyx::time_type ct;
989                 istr >> aid;
990                 istr >> ct;
991                 current_change = Change(Change::INSERTED, author_ids[aid], ct);
992         } else if (token == "\\change_deleted") {
993                 lex.nextToken();
994                 istringstream istr(lex.getString());
995                 int aid;
996                 lyx::time_type ct;
997                 istr >> aid;
998                 istr >> ct;
999                 current_change = Change(Change::DELETED, author_ids[aid], ct);
1000         } else if (token == "\\bibitem") {  // ale970302
1001                 if (!par->bibkey) {
1002                         InsetCommandParams p("bibitem", "dummy");
1003                         par->bibkey = new InsetBibKey(p);
1004                 }
1005                 par->bibkey->read(this, lex);
1006         } else if (token == "\\the_end") {
1007                 the_end_read = true;
1008         } else {
1009                 // This should be insurance for the future: (Asger)
1010                 ++unknown_tokens;
1011                 lex.eatLine();
1012 #if USE_BOOST_FORMAT
1013                 boost::format fmt(_("Unknown token: %1$s %2$s\n"));
1014                 fmt % token % lex.text();
1015                 string const s = fmt.str();
1016 #else
1017                 string const s = _("Unknown token: ") + token
1018                         + ' ' + lex.text() + '\n';
1019 #endif
1020                 // we can do this here this way because we're actually reading
1021                 // the buffer and don't care about LyXText right now.
1022                 InsetError * new_inset = new InsetError(s);
1023                 par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
1024                                  params.language));
1025
1026         }
1027
1028         return the_end_read;
1029 }
1030
1031
1032 // needed to insert the selection
1033 void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
1034                                  LyXFont const & fn,string const & str) const
1035 {
1036         LyXLayout_ptr const & layout = par->layout();
1037
1038         LyXFont font = fn;
1039
1040         par->checkInsertChar(font);
1041         // insert the string, don't insert doublespace
1042         bool space_inserted = true;
1043         bool autobreakrows = !par->inInset() ||
1044                 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
1045         for(string::const_iterator cit = str.begin();
1046             cit != str.end(); ++cit) {
1047                 if (*cit == '\n') {
1048                         if (autobreakrows && (!par->empty() || layout->keepempty)) {
1049                                 breakParagraph(params, par, pos,
1050                                                layout->isEnvironment());
1051                                 par = par->next();
1052                                 pos = 0;
1053                                 space_inserted = true;
1054                         } else {
1055                                 continue;
1056                         }
1057                         // do not insert consecutive spaces if !free_spacing
1058                 } else if ((*cit == ' ' || *cit == '\t') &&
1059                            space_inserted && !layout->free_spacing &&
1060                                    !par->isFreeSpacing())
1061                 {
1062                         continue;
1063                 } else if (*cit == '\t') {
1064                         if (!layout->free_spacing && !par->isFreeSpacing()) {
1065                                 // tabs are like spaces here
1066                                 par->insertChar(pos, ' ', font, current_change);
1067                                 ++pos;
1068                                 space_inserted = true;
1069                         } else {
1070                                 const pos_type nb = 8 - pos % 8;
1071                                 for (pos_type a = 0; a < nb ; ++a) {
1072                                         par->insertChar(pos, ' ', font, current_change);
1073                                         ++pos;
1074                                 }
1075                                 space_inserted = true;
1076                         }
1077                 } else if (!IsPrintable(*cit)) {
1078                         // Ignore unprintables
1079                         continue;
1080                 } else {
1081                         // just insert the character
1082                         par->insertChar(pos, *cit, font);
1083                         ++pos;
1084                         space_inserted = (*cit == ' ');
1085                 }
1086
1087         }
1088 }
1089
1090
1091 void Buffer::readInset(LyXLex & lex, Paragraph *& par,
1092                        int & pos, LyXFont & font)
1093 {
1094         // consistency check
1095         if (lex.getString() != "\\begin_inset") {
1096                 lyxerr << "Buffer::readInset: Consistency check failed."
1097                        << endl;
1098         }
1099
1100         Inset * inset = 0;
1101
1102         lex.next();
1103         string const tmptok = lex.getString();
1104         last_inset_read = tmptok;
1105
1106         // test the different insets
1107         if (tmptok == "LatexCommand") {
1108                 InsetCommandParams inscmd;
1109                 inscmd.read(lex);
1110
1111                 string const cmdName = inscmd.getCmdName();
1112
1113                 // This strange command allows LyX to recognize "natbib" style
1114                 // citations: citet, citep, Citet etc.
1115                 if (compare_ascii_no_case(cmdName.substr(0,4), "cite") == 0) {
1116                         inset = new InsetCitation(inscmd);
1117                 } else if (cmdName == "bibitem") {
1118                         lex.printError("Wrong place for bibitem");
1119                         inset = new InsetBibKey(inscmd);
1120                 } else if (cmdName == "BibTeX") {
1121                         inset = new InsetBibtex(inscmd);
1122                 } else if (cmdName == "index") {
1123                         inset = new InsetIndex(inscmd);
1124                 } else if (cmdName == "include") {
1125                         inset = new InsetInclude(inscmd, *this);
1126                 } else if (cmdName == "label") {
1127                         inset = new InsetLabel(inscmd);
1128                 } else if (cmdName == "url"
1129                            || cmdName == "htmlurl") {
1130                         inset = new InsetUrl(inscmd);
1131                 } else if (cmdName == "ref"
1132                            || cmdName == "pageref"
1133                            || cmdName == "vref"
1134                            || cmdName == "vpageref"
1135                            || cmdName == "prettyref") {
1136                         if (!inscmd.getOptions().empty()
1137                             || !inscmd.getContents().empty()) {
1138                                 inset = new InsetRef(inscmd, *this);
1139                         }
1140                 } else if (cmdName == "tableofcontents") {
1141                         inset = new InsetTOC(inscmd);
1142                 } else if (cmdName == "listofalgorithms") {
1143                         inset = new InsetFloatList("algorithm");
1144                 } else if (cmdName == "listoffigures") {
1145                         inset = new InsetFloatList("figure");
1146                 } else if (cmdName == "listoftables") {
1147                         inset = new InsetFloatList("table");
1148                 } else if (cmdName == "printindex") {
1149                         inset = new InsetPrintIndex(inscmd);
1150                 } else if (cmdName == "lyxparent") {
1151                         inset = new InsetParent(inscmd, *this);
1152                 }
1153         } else {
1154                 bool alreadyread = false;
1155                 if (tmptok == "Quotes") {
1156                         inset = new InsetQuotes;
1157                 } else if (tmptok == "External") {
1158                         inset = new InsetExternal;
1159                 } else if (tmptok == "FormulaMacro") {
1160                         inset = new InsetFormulaMacro;
1161                 } else if (tmptok == "Formula") {
1162                         inset = new InsetFormula;
1163                 } else if (tmptok == "Figure") { // Backward compatibility
1164 //                      inset = new InsetFig(100, 100, *this);
1165                         inset = new InsetGraphics;
1166                 } else if (tmptok == "Graphics") {
1167                         inset = new InsetGraphics;
1168                 } else if (tmptok == "Info") {// backwards compatibility
1169                         inset = new InsetNote(this,
1170                                               lex.getLongString("\\end_inset"),
1171                                               true);
1172                         alreadyread = true;
1173                 } else if (tmptok == "Note") {
1174                         inset = new InsetNote(params);
1175                 } else if (tmptok == "Include") {
1176                         InsetCommandParams p("Include");
1177                         inset = new InsetInclude(p, *this);
1178                 } else if (tmptok == "ERT") {
1179                         inset = new InsetERT(params);
1180                 } else if (tmptok == "Tabular") {
1181                         inset = new InsetTabular(*this);
1182                 } else if (tmptok == "Text") {
1183                         inset = new InsetText(params);
1184                 } else if (tmptok == "Foot") {
1185                         inset = new InsetFoot(params);
1186                 } else if (tmptok == "Marginal") {
1187                         inset = new InsetMarginal(params);
1188                 } else if (tmptok == "OptArg") {
1189                         inset = new InsetOptArg(params);
1190                 } else if (tmptok == "Minipage") {
1191                         inset = new InsetMinipage(params);
1192                 } else if (tmptok == "Float") {
1193                         lex.next();
1194                         string tmptok = lex.getString();
1195                         inset = new InsetFloat(params, tmptok);
1196                 } else if (tmptok == "Wrap") {
1197                         lex.next();
1198                         string tmptok = lex.getString();
1199                         inset = new InsetWrap(params, tmptok);
1200 #if 0
1201                 } else if (tmptok == "List") {
1202                         inset = new InsetList;
1203                 } else if (tmptok == "Theorem") {
1204                         inset = new InsetList;
1205 #endif
1206                 } else if (tmptok == "Caption") {
1207                         inset = new InsetCaption(params);
1208                 } else if (tmptok == "FloatList") {
1209                         inset = new InsetFloatList;
1210                 }
1211
1212                 if (inset && !alreadyread) inset->read(this, lex);
1213         }
1214
1215         if (inset) {
1216                 par->insertInset(pos, inset, font, current_change);
1217                 ++pos;
1218         }
1219 }
1220
1221
1222 bool Buffer::readFile(LyXLex & lex, string const & filename, Paragraph * par)
1223 {
1224         if (lex.isOK()) {
1225                 lex.next();
1226                 string const token(lex.getString());
1227                 if (token == "\\lyxformat") { // the first token _must_ be...
1228                         lex.eatLine();
1229                         string tmp_format = lex.getString();
1230                         //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
1231                         // if present remove ".," from string.
1232                         string::size_type dot = tmp_format.find_first_of(".,");
1233                         //lyxerr << "           dot found at " << dot << endl;
1234                         if (dot != string::npos)
1235                                 tmp_format.erase(dot, 1);
1236                         file_format = strToInt(tmp_format);
1237                         //lyxerr << "format: " << file_format << endl;
1238                         if (file_format == LYX_FORMAT) {
1239                                 // current format
1240                         } else if (file_format > LYX_FORMAT) {
1241                                 // future format
1242                                 Alert::alert(_("Warning!"),
1243                                         _("The file was created with a newer version of "
1244                                         "LyX. This is likely to cause problems."));
1245
1246                         } else if (file_format < LYX_FORMAT) {
1247                                 // old formats
1248                                 if (file_format < 200) {
1249                                         Alert::alert(_("ERROR!"),
1250                                                    _("Old LyX file format found. "
1251                                                      "Use LyX 0.10.x to read this!"));
1252                                         return false;
1253                                 } else if (!filename.empty()) {
1254                                         string command =
1255                                                 LibFileSearch("lyx2lyx", "lyx2lyx");
1256                                         if (command.empty()) {
1257                                                 Alert::alert(_("ERROR!"),
1258                                                              _("Can't find conversion script."));
1259                                                 return false;
1260                                         }
1261                                         command += " -t"
1262                                                 +tostr(LYX_FORMAT) + ' '
1263                                                 + QuoteName(filename);
1264                                         lyxerr[Debug::INFO] << "Running '"
1265                                                             << command << '\''
1266                                                             << endl;
1267                                         cmd_ret const ret = RunCommand(command);
1268                                         if (ret.first) {
1269                                                 Alert::alert(_("ERROR!"),
1270                                                              _("An error occured while "
1271                                                                "running the conversion script."));
1272                                                 return false;
1273                                         }
1274                                         istringstream is(STRCONV(ret.second));
1275                                         LyXLex tmplex(0, 0);
1276                                         tmplex.setStream(is);
1277                                         return readFile(tmplex, string(), par);
1278                                 } else {
1279                                         // This code is reached if lyx2lyx failed (for
1280                                         // some reason) to change the file format of
1281                                         // the file.
1282                                         lyx::Assert(false);
1283                                         return false;
1284                                 }
1285                         }
1286                         bool the_end = readLyXformat2(lex, par);
1287                         params.setPaperStuff();
1288
1289 #if 0
1290                         // the_end was added in 213
1291                         if (file_format < 213)
1292                                 the_end = true;
1293 #endif
1294
1295                         if (!the_end) {
1296                                 Alert::alert(_("Warning!"),
1297                                            _("Reading of document is not complete"),
1298                                            _("Maybe the document is truncated"));
1299                         }
1300                         return true;
1301                 } else { // "\\lyxformat" not found
1302                         Alert::alert(_("ERROR!"), _("Not a LyX file!"));
1303                 }
1304         } else
1305                 Alert::alert(_("ERROR!"), _("Unable to read file!"));
1306         return false;
1307 }
1308
1309
1310 // Should probably be moved to somewhere else: BufferView? LyXView?
1311 bool Buffer::save() const
1312 {
1313         // We don't need autosaves in the immediate future. (Asger)
1314         resetAutosaveTimers();
1315
1316         // make a backup
1317         string s;
1318         if (lyxrc.make_backup) {
1319                 s = fileName() + '~';
1320                 if (!lyxrc.backupdir_path.empty())
1321                         s = AddName(lyxrc.backupdir_path,
1322                                     subst(os::slashify_path(s),'/','!'));
1323
1324                 // Rename is the wrong way of making a backup,
1325                 // this is the correct way.
1326                 /* truss cp fil fil2:
1327                    lstat("LyXVC3.lyx", 0xEFFFF898)                 Err#2 ENOENT
1328                    stat("LyXVC.lyx", 0xEFFFF688)                   = 0
1329                    open("LyXVC.lyx", O_RDONLY)                     = 3
1330                    open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
1331                    fstat(4, 0xEFFFF508)                            = 0
1332                    fstat(3, 0xEFFFF508)                            = 0
1333                    read(3, " # T h i s   f i l e   w".., 8192)     = 5579
1334                    write(4, " # T h i s   f i l e   w".., 5579)    = 5579
1335                    read(3, 0xEFFFD4A0, 8192)                       = 0
1336                    close(4)                                        = 0
1337                    close(3)                                        = 0
1338                    chmod("LyXVC3.lyx", 0100644)                    = 0
1339                    lseek(0, 0, SEEK_CUR)                           = 46440
1340                    _exit(0)
1341                 */
1342
1343                 // Should probably have some more error checking here.
1344                 // Doing it this way, also makes the inodes stay the same.
1345                 // This is still not a very good solution, in particular we
1346                 // might loose the owner of the backup.
1347                 FileInfo finfo(fileName());
1348                 if (finfo.exist()) {
1349                         mode_t fmode = finfo.getMode();
1350                         struct utimbuf times = {
1351                                 finfo.getAccessTime(),
1352                                 finfo.getModificationTime() };
1353
1354                         ifstream ifs(fileName().c_str());
1355                         ofstream ofs(s.c_str(), ios::out|ios::trunc);
1356                         if (ifs && ofs) {
1357                                 ofs << ifs.rdbuf();
1358                                 ifs.close();
1359                                 ofs.close();
1360                                 ::chmod(s.c_str(), fmode);
1361
1362                                 if (::utime(s.c_str(), &times)) {
1363                                         lyxerr << "utime error." << endl;
1364                                 }
1365                         } else {
1366                                 lyxerr << "LyX was not able to make "
1367                                         "backup copy. Beware." << endl;
1368                         }
1369                 }
1370         }
1371
1372         if (writeFile(fileName())) {
1373                 markClean();
1374                 removeAutosaveFile(fileName());
1375         } else {
1376                 // Saving failed, so backup is not backup
1377                 if (lyxrc.make_backup) {
1378                         lyx::rename(s, fileName());
1379                 }
1380                 return false;
1381         }
1382         return true;
1383 }
1384
1385
1386 bool Buffer::writeFile(string const & fname) const
1387 {
1388         if (read_only && (fname == fileName())) {
1389                 return false;
1390         }
1391
1392         FileInfo finfo(fname);
1393         if (finfo.exist() && !finfo.writable()) {
1394                 return false;
1395         }
1396
1397         ofstream ofs(fname.c_str());
1398         if (!ofs) {
1399                 return false;
1400         }
1401
1402 #ifdef HAVE_LOCALE
1403         // Use the standard "C" locale for file output.
1404         ofs.imbue(std::locale::classic());
1405 #endif
1406
1407         // The top of the file should not be written by params.
1408
1409         // write out a comment in the top of the file
1410         ofs << '#' << lyx_docversion
1411             << " created this file. For more info see http://www.lyx.org/\n"
1412             << "\\lyxformat " << LYX_FORMAT << "\n";
1413
1414         // now write out the buffer paramters.
1415         params.writeFile(ofs);
1416
1417         // if we're tracking, list all possible authors
1418         if (params.tracking_changes) {
1419                 AuthorList::Authors::const_iterator it = authorlist.begin();
1420                 AuthorList::Authors::const_iterator end = authorlist.end();
1421                 for (; it != end; ++it) {
1422                         ofs << "\\author " << it->second << "\n";
1423                 }
1424         }
1425  
1426         Paragraph::depth_type depth = 0;
1427
1428         // this will write out all the paragraphs
1429         // using recursive descent.
1430         ParagraphList::iterator pit = paragraphs.begin();
1431         ParagraphList::iterator pend = paragraphs.end();
1432         for (; pit != pend; ++pit)
1433                 pit->write(this, ofs, params, depth);
1434
1435         // Write marker that shows file is complete
1436         ofs << "\n\\the_end" << endl;
1437
1438         ofs.close();
1439
1440         // how to check if close went ok?
1441         // Following is an attempt... (BE 20001011)
1442
1443         // good() returns false if any error occured, including some
1444         //        formatting error.
1445         // bad()  returns true if something bad happened in the buffer,
1446         //        which should include file system full errors.
1447
1448         bool status = true;
1449         if (!ofs.good()) {
1450                 status = false;
1451 #if 0
1452                 if (ofs.bad()) {
1453                         lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1454                 } else {
1455                         lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1456                                << endl;
1457                 }
1458 #endif
1459         }
1460
1461         return status;
1462 }
1463
1464
1465 namespace {
1466
1467 pair<int, string> const addDepth(int depth, int ldepth)
1468 {
1469         int d = depth * 2;
1470         if (ldepth > depth)
1471                 d += (ldepth - depth) * 2;
1472         return make_pair(d, string(d, ' '));
1473 }
1474
1475 }
1476
1477
1478 string const Buffer::asciiParagraph(Paragraph const & par,
1479                                     unsigned int linelen,
1480                                     bool noparbreak) const
1481 {
1482         ostringstream buffer;
1483         Paragraph::depth_type depth = 0;
1484         int ltype = 0;
1485         Paragraph::depth_type ltype_depth = 0;
1486         bool ref_printed = false;
1487 //      if (!par->previous()) {
1488 #if 0
1489         // begins or ends a deeper area ?
1490         if (depth != par->params().depth()) {
1491                 if (par->params().depth() > depth) {
1492                         while (par->params().depth() > depth) {
1493                                 ++depth;
1494                         }
1495                 } else {
1496                         while (par->params().depth() < depth) {
1497                                 --depth;
1498                         }
1499                 }
1500         }
1501 #else
1502         depth = par.params().depth();
1503 #endif
1504
1505         // First write the layout
1506         string const & tmp = par.layout()->name();
1507         if (compare_no_case(tmp, "itemize") == 0) {
1508                 ltype = 1;
1509                 ltype_depth = depth + 1;
1510         } else if (compare_ascii_no_case(tmp, "enumerate") == 0) {
1511                 ltype = 2;
1512                 ltype_depth = depth + 1;
1513         } else if (contains(ascii_lowercase(tmp), "ection")) {
1514                 ltype = 3;
1515                 ltype_depth = depth + 1;
1516         } else if (contains(ascii_lowercase(tmp), "aragraph")) {
1517                 ltype = 4;
1518                 ltype_depth = depth + 1;
1519         } else if (compare_ascii_no_case(tmp, "description") == 0) {
1520                 ltype = 5;
1521                 ltype_depth = depth + 1;
1522         } else if (compare_ascii_no_case(tmp, "abstract") == 0) {
1523                 ltype = 6;
1524                 ltype_depth = 0;
1525         } else if (compare_ascii_no_case(tmp, "bibliography") == 0) {
1526                 ltype = 7;
1527                 ltype_depth = 0;
1528         } else {
1529                 ltype = 0;
1530                 ltype_depth = 0;
1531         }
1532
1533         /* maybe some vertical spaces */
1534
1535         /* the labelwidthstring used in lists */
1536
1537         /* some lines? */
1538
1539         /* some pagebreaks? */
1540
1541         /* noindent ? */
1542
1543         /* what about the alignment */
1544 //      } else {
1545 //              lyxerr << "Should this ever happen?" << endl;
1546 //      }
1547
1548         // linelen <= 0 is special and means we don't have paragraph breaks
1549
1550         string::size_type currlinelen = 0;
1551
1552         if (!noparbreak) {
1553                 if (linelen > 0)
1554                         buffer << "\n\n";
1555
1556                 buffer << string(depth * 2, ' ');
1557                 currlinelen += depth * 2;
1558
1559                 //--
1560                 // we should probably change to the paragraph language in the
1561                 // gettext here (if possible) so that strings are outputted in
1562                 // the correct language! (20012712 Jug)
1563                 //--
1564                 switch (ltype) {
1565                 case 0: // Standard
1566                 case 4: // (Sub)Paragraph
1567                 case 5: // Description
1568                         break;
1569                 case 6: // Abstract
1570                         if (linelen > 0) {
1571                                 buffer << _("Abstract") << "\n\n";
1572                                 currlinelen = 0;
1573                         } else {
1574                                 string const abst = _("Abstract: ");
1575                                 buffer << abst;
1576                                 currlinelen += abst.length();
1577                         }
1578                         break;
1579                 case 7: // Bibliography
1580                         if (!ref_printed) {
1581                                 if (linelen > 0) {
1582                                         buffer << _("References") << "\n\n";
1583                                         currlinelen = 0;
1584                                 } else {
1585                                         string const refs = _("References: ");
1586                                         buffer << refs;
1587                                         currlinelen += refs.length();
1588                                 }
1589
1590                                 ref_printed = true;
1591                         }
1592                         break;
1593                 default:
1594                 {
1595                         string const parlab = par.params().labelString();
1596                         buffer << parlab << ' ';
1597                         currlinelen += parlab.length() + 1;
1598                 }
1599                 break;
1600
1601                 }
1602         }
1603
1604         if (!currlinelen) {
1605                 pair<int, string> p = addDepth(depth, ltype_depth);
1606                 buffer << p.second;
1607                 currlinelen += p.first;
1608         }
1609
1610         // this is to change the linebreak to do it by word a bit more
1611         // intelligent hopefully! (only in the case where we have a
1612         // max linelenght!) (Jug)
1613
1614         string word;
1615
1616         for (pos_type i = 0; i < par.size(); ++i) {
1617                 char c = par.getUChar(params, i);
1618                 switch (c) {
1619                 case Paragraph::META_INSET:
1620                 {
1621                         Inset const * inset = par.getInset(i);
1622                         if (inset) {
1623                                 if (linelen > 0) {
1624                                         buffer << word;
1625                                         currlinelen += word.length();
1626                                         word.erase();
1627                                 }
1628                                 if (inset->ascii(this, buffer, linelen)) {
1629                                         // to be sure it breaks paragraph
1630                                         currlinelen += linelen;
1631                                 }
1632                         }
1633                 }
1634                 break;
1635
1636                 case Paragraph::META_NEWLINE:
1637                         if (linelen > 0) {
1638                                 buffer << word << "\n";
1639                                 word.erase();
1640
1641                                 pair<int, string> p = addDepth(depth,
1642                                                                ltype_depth);
1643                                 buffer << p.second;
1644                                 currlinelen = p.first;
1645                         }
1646                         break;
1647
1648                 case Paragraph::META_HFILL:
1649                         buffer << word << "\t";
1650                         currlinelen += word.length() + 1;
1651                         word.erase();
1652                         break;
1653
1654                 default:
1655                         if (c == ' ') {
1656                                 if (linelen > 0 &&
1657                                     currlinelen + word.length() > linelen - 10) {
1658                                         buffer << "\n";
1659                                         pair<int, string> p =
1660                                                 addDepth(depth, ltype_depth);
1661                                         buffer << p.second;
1662                                         currlinelen = p.first;
1663                                 }
1664
1665                                 buffer << word << ' ';
1666                                 currlinelen += word.length() + 1;
1667                                 word.erase();
1668
1669                         } else {
1670                                 if (c != '\0') {
1671                                         word += c;
1672                                 } else {
1673                                         lyxerr[Debug::INFO] <<
1674                                                 "writeAsciiFile: NULL char in structure." << endl;
1675                                 }
1676                                 if ((linelen > 0) &&
1677                                         (currlinelen + word.length()) > linelen)
1678                                 {
1679                                         buffer << "\n";
1680
1681                                         pair<int, string> p =
1682                                                 addDepth(depth, ltype_depth);
1683                                         buffer << p.second;
1684                                         currlinelen = p.first;
1685                                 }
1686                         }
1687                         break;
1688                 }
1689         }
1690         buffer << word;
1691         return STRCONV(buffer.str());
1692 }
1693
1694
1695 void Buffer::writeFileAscii(string const & fname, int linelen)
1696 {
1697         ofstream ofs(fname.c_str());
1698         if (!ofs) {
1699                 Alert::err_alert(_("Error: Cannot write file:"), fname);
1700                 return;
1701         }
1702         writeFileAscii(ofs, linelen);
1703 }
1704
1705
1706 void Buffer::writeFileAscii(ostream & os, int linelen)
1707 {
1708         ParagraphList::iterator beg = paragraphs.begin();
1709         ParagraphList::iterator end = paragraphs.end();
1710         ParagraphList::iterator it = beg;
1711         for (; it != end; ++it) {
1712                 os << asciiParagraph(*it, linelen, it == beg);
1713         }
1714         os << "\n";
1715 }
1716
1717
1718 bool use_babel;
1719
1720
1721 void Buffer::makeLaTeXFile(string const & fname,
1722                            string const & original_path,
1723                            bool nice, bool only_body, bool only_preamble)
1724 {
1725         lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
1726
1727         ofstream ofs(fname.c_str());
1728         if (!ofs) {
1729                 Alert::err_alert(_("Error: Cannot open file: "), fname);
1730                 return;
1731         }
1732
1733         makeLaTeXFile(ofs, original_path, nice, only_body, only_preamble);
1734
1735         ofs.close();
1736         if (ofs.fail()) {
1737                 lyxerr << "File was not closed properly." << endl;
1738         }
1739 }
1740
1741
1742 void Buffer::makeLaTeXFile(ostream & os,
1743                            string const & original_path,
1744                            bool nice, bool only_body, bool only_preamble)
1745 {
1746         niceFile = nice; // this will be used by Insetincludes.
1747
1748         // validate the buffer.
1749         lyxerr[Debug::LATEX] << "  Validating buffer..." << endl;
1750         LaTeXFeatures features(params);
1751         validate(features);
1752         lyxerr[Debug::LATEX] << "  Buffer validation done." << endl;
1753
1754         texrow.reset();
1755         // The starting paragraph of the coming rows is the
1756         // first paragraph of the document. (Asger)
1757         texrow.start(&*(paragraphs.begin()), 0);
1758
1759         if (!only_body && nice) {
1760                 os << "%% " << lyx_docversion << " created this file.  "
1761                         "For more info, see http://www.lyx.org/.\n"
1762                         "%% Do not edit unless you really know what "
1763                         "you are doing.\n";
1764                 texrow.newline();
1765                 texrow.newline();
1766         }
1767         lyxerr[Debug::INFO] << "lyx header finished" << endl;
1768         // There are a few differences between nice LaTeX and usual files:
1769         // usual is \batchmode and has a
1770         // special input@path to allow the including of figures
1771         // with either \input or \includegraphics (what figinsets do).
1772         // input@path is set when the actual parameter
1773         // original_path is set. This is done for usual tex-file, but not
1774         // for nice-latex-file. (Matthias 250696)
1775         if (!only_body) {
1776                 if (!nice) {
1777                         // code for usual, NOT nice-latex-file
1778                         os << "\\batchmode\n"; // changed
1779                         // from \nonstopmode
1780                         texrow.newline();
1781                 }
1782                 if (!original_path.empty()) {
1783                         string inputpath = os::external_path(original_path);
1784                         subst(inputpath, "~", "\\string~");
1785                         os << "\\makeatletter\n"
1786                             << "\\def\\input@path{{"
1787                             << inputpath << "/}}\n"
1788                             << "\\makeatother\n";
1789                         texrow.newline();
1790                         texrow.newline();
1791                         texrow.newline();
1792                 }
1793
1794                 os << "\\documentclass";
1795
1796                 LyXTextClass const & tclass = params.getLyXTextClass();
1797
1798                 ostringstream options; // the document class options.
1799
1800                 if (tokenPos(tclass.opt_fontsize(),
1801                              '|', params.fontsize) >= 0) {
1802                         // only write if existing in list (and not default)
1803                         options << params.fontsize << "pt,";
1804                 }
1805
1806
1807                 if (!params.use_geometry &&
1808                     (params.paperpackage == BufferParams::PACKAGE_NONE)) {
1809                         switch (params.papersize) {
1810                         case BufferParams::PAPER_A4PAPER:
1811                                 options << "a4paper,";
1812                                 break;
1813                         case BufferParams::PAPER_USLETTER:
1814                                 options << "letterpaper,";
1815                                 break;
1816                         case BufferParams::PAPER_A5PAPER:
1817                                 options << "a5paper,";
1818                                 break;
1819                         case BufferParams::PAPER_B5PAPER:
1820                                 options << "b5paper,";
1821                                 break;
1822                         case BufferParams::PAPER_EXECUTIVEPAPER:
1823                                 options << "executivepaper,";
1824                                 break;
1825                         case BufferParams::PAPER_LEGALPAPER:
1826                                 options << "legalpaper,";
1827                                 break;
1828                         }
1829                 }
1830
1831                 // if needed
1832                 if (params.sides != tclass.sides()) {
1833                         switch (params.sides) {
1834                         case LyXTextClass::OneSide:
1835                                 options << "oneside,";
1836                                 break;
1837                         case LyXTextClass::TwoSides:
1838                                 options << "twoside,";
1839                                 break;
1840                         }
1841                 }
1842
1843                 // if needed
1844                 if (params.columns != tclass.columns()) {
1845                         if (params.columns == 2)
1846                                 options << "twocolumn,";
1847                         else
1848                                 options << "onecolumn,";
1849                 }
1850
1851                 if (!params.use_geometry
1852                     && params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
1853                         options << "landscape,";
1854
1855                 // language should be a parameter to \documentclass
1856                 use_babel = false;
1857                 ostringstream language_options;
1858                 if (params.language->babel() == "hebrew"
1859                     && default_language->babel() != "hebrew")
1860                          // This seems necessary
1861                         features.useLanguage(default_language);
1862
1863                 if (lyxrc.language_use_babel ||
1864                     params.language->lang() != lyxrc.default_language ||
1865                     features.hasLanguages()) {
1866                         use_babel = true;
1867                         language_options << features.getLanguages();
1868                         language_options << params.language->babel();
1869                         if (lyxrc.language_global_options)
1870                                 options << language_options.str() << ',';
1871                 }
1872
1873                 // the user-defined options
1874                 if (!params.options.empty()) {
1875                         options << params.options << ',';
1876                 }
1877
1878                 string strOptions(STRCONV(options.str()));
1879                 if (!strOptions.empty()) {
1880                         strOptions = rtrim(strOptions, ",");
1881                         os << '[' << strOptions << ']';
1882                 }
1883
1884                 os << '{' << tclass.latexname() << "}\n";
1885                 texrow.newline();
1886                 // end of \documentclass defs
1887
1888                 // font selection must be done before loading fontenc.sty
1889                 // The ae package is not needed when using OT1 font encoding.
1890                 if (params.fonts != "default" &&
1891                     (params.fonts != "ae" || lyxrc.fontenc != "default")) {
1892                         os << "\\usepackage{" << params.fonts << "}\n";
1893                         texrow.newline();
1894                         if (params.fonts == "ae") {
1895                                 os << "\\usepackage{aecompl}\n";
1896                                 texrow.newline();
1897                         }
1898                 }
1899                 // this one is not per buffer
1900                 if (lyxrc.fontenc != "default") {
1901                         os << "\\usepackage[" << lyxrc.fontenc
1902                             << "]{fontenc}\n";
1903                         texrow.newline();
1904                 }
1905
1906                 if (params.inputenc == "auto") {
1907                         string const doc_encoding =
1908                                 params.language->encoding()->LatexName();
1909
1910                         // Create a list with all the input encodings used
1911                         // in the document
1912                         set<string> encodings = features.getEncodingSet(doc_encoding);
1913
1914                         os << "\\usepackage[";
1915                         std::copy(encodings.begin(), encodings.end(),
1916                                   std::ostream_iterator<string>(os, ","));
1917                         os << doc_encoding << "]{inputenc}\n";
1918                         texrow.newline();
1919                 } else if (params.inputenc != "default") {
1920                         os << "\\usepackage[" << params.inputenc
1921                             << "]{inputenc}\n";
1922                         texrow.newline();
1923                 }
1924
1925                 // At the very beginning the text parameters.
1926                 if (params.paperpackage != BufferParams::PACKAGE_NONE) {
1927                         switch (params.paperpackage) {
1928                         case BufferParams::PACKAGE_A4:
1929                                 os << "\\usepackage{a4}\n";
1930                                 texrow.newline();
1931                                 break;
1932                         case BufferParams::PACKAGE_A4WIDE:
1933                                 os << "\\usepackage{a4wide}\n";
1934                                 texrow.newline();
1935                                 break;
1936                         case BufferParams::PACKAGE_WIDEMARGINSA4:
1937                                 os << "\\usepackage[widemargins]{a4}\n";
1938                                 texrow.newline();
1939                                 break;
1940                         }
1941                 }
1942                 if (params.use_geometry) {
1943                         os << "\\usepackage{geometry}\n";
1944                         texrow.newline();
1945                         os << "\\geometry{verbose";
1946                         if (params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
1947                                 os << ",landscape";
1948                         switch (params.papersize2) {
1949                         case BufferParams::VM_PAPER_CUSTOM:
1950                                 if (!params.paperwidth.empty())
1951                                         os << ",paperwidth="
1952                                             << params.paperwidth;
1953                                 if (!params.paperheight.empty())
1954                                         os << ",paperheight="
1955                                             << params.paperheight;
1956                                 break;
1957                         case BufferParams::VM_PAPER_USLETTER:
1958                                 os << ",letterpaper";
1959                                 break;
1960                         case BufferParams::VM_PAPER_USLEGAL:
1961                                 os << ",legalpaper";
1962                                 break;
1963                         case BufferParams::VM_PAPER_USEXECUTIVE:
1964                                 os << ",executivepaper";
1965                                 break;
1966                         case BufferParams::VM_PAPER_A3:
1967                                 os << ",a3paper";
1968                                 break;
1969                         case BufferParams::VM_PAPER_A4:
1970                                 os << ",a4paper";
1971                                 break;
1972                         case BufferParams::VM_PAPER_A5:
1973                                 os << ",a5paper";
1974                                 break;
1975                         case BufferParams::VM_PAPER_B3:
1976                                 os << ",b3paper";
1977                                 break;
1978                         case BufferParams::VM_PAPER_B4:
1979                                 os << ",b4paper";
1980                                 break;
1981                         case BufferParams::VM_PAPER_B5:
1982                                 os << ",b5paper";
1983                                 break;
1984                         default:
1985                                 // default papersize ie BufferParams::VM_PAPER_DEFAULT
1986                                 switch (lyxrc.default_papersize) {
1987                                 case BufferParams::PAPER_DEFAULT: // keep compiler happy
1988                                 case BufferParams::PAPER_USLETTER:
1989                                         os << ",letterpaper";
1990                                         break;
1991                                 case BufferParams::PAPER_LEGALPAPER:
1992                                         os << ",legalpaper";
1993                                         break;
1994                                 case BufferParams::PAPER_EXECUTIVEPAPER:
1995                                         os << ",executivepaper";
1996                                         break;
1997                                 case BufferParams::PAPER_A3PAPER:
1998                                         os << ",a3paper";
1999                                         break;
2000                                 case BufferParams::PAPER_A4PAPER:
2001                                         os << ",a4paper";
2002                                         break;
2003                                 case BufferParams::PAPER_A5PAPER:
2004                                         os << ",a5paper";
2005                                         break;
2006                                 case BufferParams::PAPER_B5PAPER:
2007                                         os << ",b5paper";
2008                                         break;
2009                                 }
2010                         }
2011                         if (!params.topmargin.empty())
2012                                 os << ",tmargin=" << params.topmargin;
2013                         if (!params.bottommargin.empty())
2014                                 os << ",bmargin=" << params.bottommargin;
2015                         if (!params.leftmargin.empty())
2016                                 os << ",lmargin=" << params.leftmargin;
2017                         if (!params.rightmargin.empty())
2018                                 os << ",rmargin=" << params.rightmargin;
2019                         if (!params.headheight.empty())
2020                                 os << ",headheight=" << params.headheight;
2021                         if (!params.headsep.empty())
2022                                 os << ",headsep=" << params.headsep;
2023                         if (!params.footskip.empty())
2024                                 os << ",footskip=" << params.footskip;
2025                         os << "}\n";
2026                         texrow.newline();
2027                 }
2028
2029                 if (tokenPos(tclass.opt_pagestyle(),
2030                              '|', params.pagestyle) >= 0) {
2031                         if (params.pagestyle == "fancy") {
2032                                 os << "\\usepackage{fancyhdr}\n";
2033                                 texrow.newline();
2034                         }
2035                         os << "\\pagestyle{" << params.pagestyle << "}\n";
2036                         texrow.newline();
2037                 }
2038
2039                 if (params.secnumdepth != tclass.secnumdepth()) {
2040                         os << "\\setcounter{secnumdepth}{"
2041                             << params.secnumdepth
2042                             << "}\n";
2043                         texrow.newline();
2044                 }
2045                 if (params.tocdepth != tclass.tocdepth()) {
2046                         os << "\\setcounter{tocdepth}{"
2047                             << params.tocdepth
2048                             << "}\n";
2049                         texrow.newline();
2050                 }
2051
2052                 if (params.paragraph_separation) {
2053                         switch (params.defskip.kind()) {
2054                         case VSpace::SMALLSKIP:
2055                                 os << "\\setlength\\parskip{\\smallskipamount}\n";
2056                                 break;
2057                         case VSpace::MEDSKIP:
2058                                 os << "\\setlength\\parskip{\\medskipamount}\n";
2059                                 break;
2060                         case VSpace::BIGSKIP:
2061                                 os << "\\setlength\\parskip{\\bigskipamount}\n";
2062                                 break;
2063                         case VSpace::LENGTH:
2064                                 os << "\\setlength\\parskip{"
2065                                     << params.defskip.length().asLatexString()
2066                                     << "}\n";
2067                                 break;
2068                         default: // should never happen // Then delete it.
2069                                 os << "\\setlength\\parskip{\\medskipamount}\n";
2070                                 break;
2071                         }
2072                         texrow.newline();
2073
2074                         os << "\\setlength\\parindent{0pt}\n";
2075                         texrow.newline();
2076                 }
2077
2078                 // Now insert the LyX specific LaTeX commands...
2079
2080                 // The optional packages;
2081                 string preamble(features.getPackages());
2082
2083                 // this might be useful...
2084                 preamble += "\n\\makeatletter\n";
2085
2086                 // Some macros LyX will need
2087                 string tmppreamble(features.getMacros());
2088
2089                 if (!tmppreamble.empty()) {
2090                         preamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2091                                 "LyX specific LaTeX commands.\n"
2092                                 + tmppreamble + '\n';
2093                 }
2094
2095                 // the text class specific preamble
2096                 tmppreamble = features.getTClassPreamble();
2097                 if (!tmppreamble.empty()) {
2098                         preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2099                                 "Textclass specific LaTeX commands.\n"
2100                                 + tmppreamble + '\n';
2101                 }
2102
2103                 /* the user-defined preamble */
2104                 if (!params.preamble.empty()) {
2105                         preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2106                                 "User specified LaTeX commands.\n"
2107                                 + params.preamble + '\n';
2108                 }
2109
2110                 // Itemize bullet settings need to be last in case the user
2111                 // defines their own bullets that use a package included
2112                 // in the user-defined preamble -- ARRae
2113                 // Actually it has to be done much later than that
2114                 // since some packages like frenchb make modifications
2115                 // at \begin{document} time -- JMarc
2116                 string bullets_def;
2117                 for (int i = 0; i < 4; ++i) {
2118                         if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
2119                                 if (bullets_def.empty())
2120                                         bullets_def="\\AtBeginDocument{\n";
2121                                 bullets_def += "  \\renewcommand{\\labelitemi";
2122                                 switch (i) {
2123                                 // `i' is one less than the item to modify
2124                                 case 0:
2125                                         break;
2126                                 case 1:
2127                                         bullets_def += 'i';
2128                                         break;
2129                                 case 2:
2130                                         bullets_def += "ii";
2131                                         break;
2132                                 case 3:
2133                                         bullets_def += 'v';
2134                                         break;
2135                                 }
2136                                 bullets_def += "}{" +
2137                                   params.user_defined_bullets[i].getText()
2138                                   + "}\n";
2139                         }
2140                 }
2141
2142                 if (!bullets_def.empty())
2143                   preamble += bullets_def + "}\n\n";
2144
2145                 int const nlines =
2146                         int(lyx::count(preamble.begin(), preamble.end(), '\n'));
2147                 for (int j = 0; j != nlines; ++j) {
2148                         texrow.newline();
2149                 }
2150
2151                 // We try to load babel late, in case it interferes
2152                 // with other packages.
2153                 if (use_babel) {
2154                         string tmp = lyxrc.language_package;
2155                         if (!lyxrc.language_global_options
2156                             && tmp == "\\usepackage{babel}")
2157                                 tmp = string("\\usepackage[") +
2158                                         STRCONV(language_options.str()) +
2159                                         "]{babel}";
2160                         preamble += tmp + "\n";
2161                         preamble += features.getBabelOptions();
2162                 }
2163
2164                 preamble += "\\makeatother\n";
2165
2166                 // dvipost settings come after everything else
2167                 if (params.tracking_changes) {
2168                         preamble += "\\dvipostlayout\n";
2169                         preamble += "\\dvipost{osstart color push Red}\n";
2170                         preamble += "\\dvipost{osend color pop}\n";
2171                         preamble += "\\dvipost{cbstart color push Blue}\n";
2172                         preamble += "\\dvipost{cbend color pop} \n";
2173                 }
2174
2175                 os << preamble;
2176
2177                 if (only_preamble)
2178                         return;
2179
2180                 // make the body.
2181                 os << "\\begin{document}\n";
2182                 texrow.newline();
2183         } // only_body
2184         lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
2185
2186         if (!lyxrc.language_auto_begin) {
2187                 os << subst(lyxrc.language_command_begin, "$$lang",
2188                              params.language->babel())
2189                     << endl;
2190                 texrow.newline();
2191         }
2192
2193         latexParagraphs(os, &*(paragraphs.begin()), 0, texrow);
2194
2195         // add this just in case after all the paragraphs
2196         os << endl;
2197         texrow.newline();
2198
2199         if (!lyxrc.language_auto_end) {
2200                 os << subst(lyxrc.language_command_end, "$$lang",
2201                              params.language->babel())
2202                     << endl;
2203                 texrow.newline();
2204         }
2205
2206         if (!only_body) {
2207                 os << "\\end{document}\n";
2208                 texrow.newline();
2209
2210                 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
2211         } else {
2212                 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
2213                                      << endl;
2214         }
2215
2216         // Just to be sure. (Asger)
2217         texrow.newline();
2218
2219         lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
2220         lyxerr[Debug::INFO] << "Row count was " << texrow.rows() - 1
2221                             << '.' << endl;
2222
2223         // we want this to be true outside previews (for insetexternal)
2224         niceFile = true;
2225 }
2226
2227
2228 //
2229 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
2230 //
2231 void Buffer::latexParagraphs(ostream & ofs, Paragraph * par,
2232                              Paragraph * endpar, TexRow & texrow,
2233                              bool moving_arg) const
2234 {
2235         bool was_title = false;
2236         bool already_title = false;
2237
2238         // if only_body
2239         while (par != endpar) {
2240                 Inset * in = par->inInset();
2241                 // well we have to check if we are in an inset with unlimited
2242                 // length (all in one row) if that is true then we don't allow
2243                 // any special options in the paragraph and also we don't allow
2244                 // any environment other then "Standard" to be valid!
2245                 if ((in == 0) || !in->forceDefaultParagraphs(in)) {
2246                         LyXLayout_ptr const & layout = par->layout();
2247
2248                         if (layout->intitle) {
2249                                 if (already_title) {
2250                                         lyxerr <<"Error in latexParagraphs: You"
2251                                                 " should not mix title layouts"
2252                                                 " with normal ones." << endl;
2253                                 } else
2254                                         was_title = true;
2255                         } else if (was_title && !already_title) {
2256                                 ofs << "\\maketitle\n";
2257                                 texrow.newline();
2258                                 already_title = true;
2259                                 was_title = false;
2260                         }
2261
2262                         if (layout->isEnvironment() ||
2263                                 !par->params().leftIndent().zero())
2264                         {
2265                                 par = par->TeXEnvironment(this, params, ofs, texrow);
2266                         } else {
2267                                 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2268                         }
2269                 } else {
2270                         par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2271                 }
2272         }
2273         // It might be that we only have a title in this document
2274         if (was_title && !already_title) {
2275                 ofs << "\\maketitle\n";
2276                 texrow.newline();
2277         }
2278 }
2279
2280
2281 bool Buffer::isLatex() const
2282 {
2283         return params.getLyXTextClass().outputType() == LATEX;
2284 }
2285
2286
2287 bool Buffer::isLinuxDoc() const
2288 {
2289         return params.getLyXTextClass().outputType() == LINUXDOC;
2290 }
2291
2292
2293 bool Buffer::isLiterate() const
2294 {
2295         return params.getLyXTextClass().outputType() == LITERATE;
2296 }
2297
2298
2299 bool Buffer::isDocBook() const
2300 {
2301         return params.getLyXTextClass().outputType() == DOCBOOK;
2302 }
2303
2304
2305 bool Buffer::isSGML() const
2306 {
2307         LyXTextClass const & tclass = params.getLyXTextClass();
2308
2309         return tclass.outputType() == LINUXDOC ||
2310                tclass.outputType() == DOCBOOK;
2311 }
2312
2313
2314 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
2315 {
2316         ofstream ofs(fname.c_str());
2317
2318         if (!ofs) {
2319                 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2320                 return;
2321         }
2322
2323         niceFile = nice; // this will be used by included files.
2324
2325         LaTeXFeatures features(params);
2326
2327         validate(features);
2328
2329         texrow.reset();
2330
2331         LyXTextClass const & tclass = params.getLyXTextClass();
2332
2333         string top_element = tclass.latexname();
2334
2335         if (!body_only) {
2336                 ofs << "<!doctype linuxdoc system";
2337
2338                 string preamble = params.preamble;
2339                 const string name = nice ? ChangeExtension(filename_, ".sgml")
2340                          : fname;
2341                 preamble += features.getIncludedFiles(name);
2342                 preamble += features.getLyXSGMLEntities();
2343
2344                 if (!preamble.empty()) {
2345                         ofs << " [ " << preamble << " ]";
2346                 }
2347                 ofs << ">\n\n";
2348
2349                 if (params.options.empty())
2350                         sgml::openTag(ofs, 0, false, top_element);
2351                 else {
2352                         string top = top_element;
2353                         top += ' ';
2354                         top += params.options;
2355                         sgml::openTag(ofs, 0, false, top);
2356                 }
2357         }
2358
2359         ofs << "<!-- "  << lyx_docversion
2360             << " created this file. For more info see http://www.lyx.org/"
2361             << " -->\n";
2362
2363         Paragraph::depth_type depth = 0; // paragraph depth
2364         Paragraph * par = &*(paragraphs.begin());
2365         string item_name;
2366         vector<string> environment_stack(5);
2367
2368         while (par) {
2369                 LyXLayout_ptr const & style = par->layout();
2370                 // treat <toc> as a special case for compatibility with old code
2371                 if (par->isInset(0)) {
2372                         Inset * inset = par->getInset(0);
2373                         Inset::Code lyx_code = inset->lyxCode();
2374                         if (lyx_code == Inset::TOC_CODE) {
2375                                 string const temp = "toc";
2376                                 sgml::openTag(ofs, depth, false, temp);
2377
2378                                 par = par->next();
2379                                 continue;
2380                         }
2381                 }
2382
2383                 // environment tag closing
2384                 for (; depth > par->params().depth(); --depth) {
2385                         sgml::closeTag(ofs, depth, false, environment_stack[depth]);
2386                         environment_stack[depth].erase();
2387                 }
2388
2389                 // write opening SGML tags
2390                 switch (style->latextype) {
2391                 case LATEX_PARAGRAPH:
2392                         if (depth == par->params().depth()
2393                            && !environment_stack[depth].empty()) {
2394                                 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
2395                                 environment_stack[depth].erase();
2396                                 if (depth)
2397                                         --depth;
2398                                 else
2399                                         ofs << "</p>";
2400                         }
2401                         sgml::openTag(ofs, depth, false, style->latexname());
2402                         break;
2403
2404                 case LATEX_COMMAND:
2405                         if (depth!= 0)
2406                                 sgmlError(par, 0,
2407                                           _("Error: Wrong depth for LatexType Command.\n"));
2408
2409                         if (!environment_stack[depth].empty()) {
2410                                 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
2411                                 ofs << "</p>";
2412                         }
2413
2414                         environment_stack[depth].erase();
2415                         sgml::openTag(ofs, depth, false, style->latexname());
2416                         break;
2417
2418                 case LATEX_ENVIRONMENT:
2419                 case LATEX_ITEM_ENVIRONMENT:
2420                 {
2421                         string const & latexname = style->latexname();
2422
2423                         if (depth == par->params().depth()
2424                             && environment_stack[depth] != latexname) {
2425                                 sgml::closeTag(ofs, depth, false,
2426                                              environment_stack[depth]);
2427                                 environment_stack[depth].erase();
2428                         }
2429                         if (depth < par->params().depth()) {
2430                                depth = par->params().depth();
2431                                environment_stack[depth].erase();
2432                         }
2433                         if (environment_stack[depth] != latexname) {
2434                                 if (depth == 0) {
2435                                         sgml::openTag(ofs, depth, false, "p");
2436                                 }
2437                                 sgml::openTag(ofs, depth, false, latexname);
2438
2439                                 if (environment_stack.size() == depth + 1)
2440                                         environment_stack.push_back("!-- --");
2441                                 environment_stack[depth] = latexname;
2442                         }
2443
2444                         if (style->latexparam() == "CDATA")
2445                                 ofs << "<![CDATA[";
2446
2447                         if (style->latextype == LATEX_ENVIRONMENT) break;
2448
2449                         if (style->labeltype == LABEL_MANUAL)
2450                                 item_name = "tag";
2451                         else
2452                                 item_name = "item";
2453
2454                         sgml::openTag(ofs, depth + 1, false, item_name);
2455                 }
2456                 break;
2457
2458                 default:
2459                         sgml::openTag(ofs, depth, false, style->latexname());
2460                         break;
2461                 }
2462
2463                 simpleLinuxDocOnePar(ofs, par, depth);
2464
2465                 par = par->next();
2466
2467                 ofs << "\n";
2468                 // write closing SGML tags
2469                 switch (style->latextype) {
2470                 case LATEX_COMMAND:
2471                         break;
2472                 case LATEX_ENVIRONMENT:
2473                 case LATEX_ITEM_ENVIRONMENT:
2474                         if (style->latexparam() == "CDATA")
2475                                 ofs << "]]>";
2476                         break;
2477                 default:
2478                         sgml::closeTag(ofs, depth, false, style->latexname());
2479                         break;
2480                 }
2481         }
2482
2483         // Close open tags
2484         for (int i = depth; i >= 0; --i)
2485                 sgml::closeTag(ofs, depth, false, environment_stack[i]);
2486
2487         if (!body_only) {
2488                 ofs << "\n\n";
2489                 sgml::closeTag(ofs, 0, false, top_element);
2490         }
2491
2492         ofs.close();
2493         // How to check for successful close
2494
2495         // we want this to be true outside previews (for insetexternal)
2496         niceFile = true;
2497 }
2498
2499
2500 // checks, if newcol chars should be put into this line
2501 // writes newline, if necessary.
2502 namespace {
2503
2504 void sgmlLineBreak(ostream & os, string::size_type & colcount,
2505                           string::size_type newcol)
2506 {
2507         colcount += newcol;
2508         if (colcount > lyxrc.ascii_linelen) {
2509                 os << "\n";
2510                 colcount = newcol; // assume write after this call
2511         }
2512 }
2513
2514 enum PAR_TAG {
2515         NONE=0,
2516         TT = 1,
2517         SF = 2,
2518         BF = 4,
2519         IT = 8,
2520         SL = 16,
2521         EM = 32
2522 };
2523
2524
2525 string tag_name(PAR_TAG const & pt) {
2526         switch (pt) {
2527         case NONE: return "!-- --";
2528         case TT: return "tt";
2529         case SF: return "sf";
2530         case BF: return "bf";
2531         case IT: return "it";
2532         case SL: return "sl";
2533         case EM: return "em";
2534         }
2535         return "";
2536 }
2537
2538
2539 inline
2540 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2541 {
2542         p1 = static_cast<PAR_TAG>(p1 | p2);
2543 }
2544
2545
2546 inline
2547 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2548 {
2549         p1 = static_cast<PAR_TAG>(p1 & ~p2);
2550 }
2551
2552 } // anon
2553
2554
2555 // Handle internal paragraph parsing -- layout already processed.
2556 void Buffer::simpleLinuxDocOnePar(ostream & os,
2557         Paragraph * par,
2558         Paragraph::depth_type /*depth*/)
2559 {
2560         LyXLayout_ptr const & style = par->layout();
2561
2562         string::size_type char_line_count = 5;     // Heuristic choice ;-)
2563
2564         // gets paragraph main font
2565         LyXFont font_old;
2566         bool desc_on;
2567         if (style->labeltype == LABEL_MANUAL) {
2568                 font_old = style->labelfont;
2569                 desc_on = true;
2570         } else {
2571                 font_old = style->font;
2572                 desc_on = false;
2573         }
2574
2575         LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
2576         LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
2577         LyXFont::FONT_SHAPE  shape_type  = LyXFont::UP_SHAPE;
2578         bool is_em = false;
2579
2580         stack<PAR_TAG> tag_state;
2581         // parsing main loop
2582         for (pos_type i = 0; i < par->size(); ++i) {
2583
2584                 PAR_TAG tag_close = NONE;
2585                 list < PAR_TAG > tag_open;
2586
2587                 LyXFont const font = par->getFont(params, i);
2588
2589                 if (font_old.family() != font.family()) {
2590                         switch (family_type) {
2591                         case LyXFont::SANS_FAMILY:
2592                                 tag_close |= SF;
2593                                 break;
2594                         case LyXFont::TYPEWRITER_FAMILY:
2595                                 tag_close |= TT;
2596                                 break;
2597                         default:
2598                                 break;
2599                         }
2600
2601                         family_type = font.family();
2602
2603                         switch (family_type) {
2604                         case LyXFont::SANS_FAMILY:
2605                                 tag_open.push_back(SF);
2606                                 break;
2607                         case LyXFont::TYPEWRITER_FAMILY:
2608                                 tag_open.push_back(TT);
2609                                 break;
2610                         default:
2611                                 break;
2612                         }
2613                 }
2614
2615                 if (font_old.series() != font.series()) {
2616                         switch (series_type) {
2617                         case LyXFont::BOLD_SERIES:
2618                                 tag_close |= BF;
2619                                 break;
2620                         default:
2621                                 break;
2622                         }
2623
2624                         series_type = font.series();
2625
2626                         switch (series_type) {
2627                         case LyXFont::BOLD_SERIES:
2628                                 tag_open.push_back(BF);
2629                                 break;
2630                         default:
2631                                 break;
2632                         }
2633
2634                 }
2635
2636                 if (font_old.shape() != font.shape()) {
2637                         switch (shape_type) {
2638                         case LyXFont::ITALIC_SHAPE:
2639                                 tag_close |= IT;
2640                                 break;
2641                         case LyXFont::SLANTED_SHAPE:
2642                                 tag_close |= SL;
2643                                 break;
2644                         default:
2645                                 break;
2646                         }
2647
2648                         shape_type = font.shape();
2649
2650                         switch (shape_type) {
2651                         case LyXFont::ITALIC_SHAPE:
2652                                 tag_open.push_back(IT);
2653                                 break;
2654                         case LyXFont::SLANTED_SHAPE:
2655                                 tag_open.push_back(SL);
2656                                 break;
2657                         default:
2658                                 break;
2659                         }
2660                 }
2661                 // handle <em> tag
2662                 if (font_old.emph() != font.emph()) {
2663                         if (font.emph() == LyXFont::ON) {
2664                                 tag_open.push_back(EM);
2665                                 is_em = true;
2666                         }
2667                         else if (is_em) {
2668                                 tag_close |= EM;
2669                                 is_em = false;
2670                         }
2671                 }
2672
2673                 list < PAR_TAG > temp;
2674                 while (!tag_state.empty() && tag_close) {
2675                         PAR_TAG k =  tag_state.top();
2676                         tag_state.pop();
2677                         os << "</" << tag_name(k) << '>';
2678                         if (tag_close & k)
2679                                 reset(tag_close,k);
2680                         else
2681                                 temp.push_back(k);
2682                 }
2683
2684                 for(list< PAR_TAG >::const_iterator j = temp.begin();
2685                     j != temp.end(); ++j) {
2686                         tag_state.push(*j);
2687                         os << '<' << tag_name(*j) << '>';
2688                 }
2689
2690                 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
2691                     j != tag_open.end(); ++j) {
2692                         tag_state.push(*j);
2693                         os << '<' << tag_name(*j) << '>';
2694                 }
2695
2696                 char c = par->getChar(i);
2697
2698                 if (c == Paragraph::META_INSET) {
2699                         Inset * inset = par->getInset(i);
2700                         inset->linuxdoc(this, os);
2701                         font_old = font;
2702                         continue;
2703                 }
2704
2705                 if (style->latexparam() == "CDATA") {
2706                         // "TeX"-Mode on == > SGML-Mode on.
2707                         if (c != '\0')
2708                                 os << c;
2709                         ++char_line_count;
2710                 } else {
2711                         bool ws;
2712                         string str;
2713                         boost::tie(ws, str) = sgml::escapeChar(c);
2714                         if (ws && !style->free_spacing && !par->isFreeSpacing()) {
2715                                 // in freespacing mode, spaces are
2716                                 // non-breaking characters
2717                                 if (desc_on) {// if char is ' ' then...
2718
2719                                         ++char_line_count;
2720                                         sgmlLineBreak(os, char_line_count, 6);
2721                                         os << "</tag>";
2722                                         desc_on = false;
2723                                 } else  {
2724                                         sgmlLineBreak(os, char_line_count, 1);
2725                                         os << c;
2726                                 }
2727                         } else {
2728                                 os << str;
2729                                 char_line_count += str.length();
2730                         }
2731                 }
2732                 font_old = font;
2733         }
2734
2735         while (!tag_state.empty()) {
2736                 os << "</" << tag_name(tag_state.top()) << '>';
2737                 tag_state.pop();
2738         }
2739
2740         // resets description flag correctly
2741         if (desc_on) {
2742                 // <tag> not closed...
2743                 sgmlLineBreak(os, char_line_count, 6);
2744                 os << "</tag>";
2745         }
2746 }
2747
2748
2749 // Print an error message.
2750 void Buffer::sgmlError(Paragraph * /*par*/, int /*pos*/,
2751         string const & /*message*/) const
2752 {
2753 #ifdef WITH_WARNINGS
2754 #warning This is wrong we cannot insert an inset like this!!!
2755         // I guess this was Jose' so I explain you more or less why this
2756         // is wrong. This way you insert something in the paragraph and
2757         // don't tell it to LyXText (row rebreaking and undo handling!!!)
2758         // I deactivate this code, have a look at BufferView::insertErrors
2759         // how you should do this correctly! (Jug 20020315)
2760 #endif
2761 #if 0
2762         // insert an error marker in text
2763         InsetError * new_inset = new InsetError(message);
2764         par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
2765                          params.language));
2766 #endif
2767 }
2768
2769
2770 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
2771 {
2772         ofstream ofs(fname.c_str());
2773         if (!ofs) {
2774                 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2775                 return;
2776         }
2777
2778         Paragraph * par = &*(paragraphs.begin());
2779
2780         niceFile = nice; // this will be used by Insetincludes.
2781
2782         LaTeXFeatures features(params);
2783         validate(features);
2784
2785         texrow.reset();
2786
2787         LyXTextClass const & tclass = params.getLyXTextClass();
2788         string top_element = tclass.latexname();
2789
2790         if (!only_body) {
2791                 ofs << "<!DOCTYPE " << top_element
2792                     << "  PUBLIC \"-//OASIS//DTD DocBook V4.1//EN\"";
2793
2794                 string preamble = params.preamble;
2795                 const string name = nice ? ChangeExtension(filename_, ".sgml")
2796                          : fname;
2797                 preamble += features.getIncludedFiles(name);
2798                 preamble += features.getLyXSGMLEntities();
2799
2800                 if (!preamble.empty()) {
2801                         ofs << "\n [ " << preamble << " ]";
2802                 }
2803                 ofs << ">\n\n";
2804         }
2805
2806         string top = top_element;
2807         top += " lang=\"";
2808         top += params.language->code();
2809         top += '"';
2810
2811         if (!params.options.empty()) {
2812                 top += ' ';
2813                 top += params.options;
2814         }
2815         sgml::openTag(ofs, 0, false, top);
2816
2817         ofs << "<!-- DocBook file was created by " << lyx_docversion
2818             << "\n  See http://www.lyx.org/ for more information -->\n";
2819
2820         vector<string> environment_stack(10);
2821         vector<string> environment_inner(10);
2822         vector<string> command_stack(10);
2823
2824         bool command_flag = false;
2825         Paragraph::depth_type command_depth = 0;
2826         Paragraph::depth_type command_base = 0;
2827         Paragraph::depth_type cmd_depth = 0;
2828         Paragraph::depth_type depth = 0; // paragraph depth
2829
2830         string item_name;
2831         string command_name;
2832
2833         while (par) {
2834                 string sgmlparam;
2835                 string c_depth;
2836                 string c_params;
2837                 int desc_on = 0; // description mode
2838
2839                 LyXLayout_ptr const & style = par->layout();
2840
2841                 // environment tag closing
2842                 for (; depth > par->params().depth(); --depth) {
2843                         if (environment_inner[depth] != "!-- --") {
2844                                 item_name = "listitem";
2845                                 sgml::closeTag(ofs, command_depth + depth, false, item_name);
2846                                 if (environment_inner[depth] == "varlistentry")
2847                                         sgml::closeTag(ofs, depth+command_depth, false, environment_inner[depth]);
2848                         }
2849                         sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2850                         environment_stack[depth].erase();
2851                         environment_inner[depth].erase();
2852                 }
2853
2854                 if (depth == par->params().depth()
2855                    && environment_stack[depth] != style->latexname()
2856                    && !environment_stack[depth].empty()) {
2857                         if (environment_inner[depth] != "!-- --") {
2858                                 item_name= "listitem";
2859                                 sgml::closeTag(ofs, command_depth+depth, false, item_name);
2860                                 if (environment_inner[depth] == "varlistentry")
2861                                         sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
2862                         }
2863
2864                         sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2865
2866                         environment_stack[depth].erase();
2867                         environment_inner[depth].erase();
2868                 }
2869
2870                 // Write opening SGML tags.
2871                 switch (style->latextype) {
2872                 case LATEX_PARAGRAPH:
2873                         sgml::openTag(ofs, depth + command_depth,
2874                                     false, style->latexname());
2875                         break;
2876
2877                 case LATEX_COMMAND:
2878                         if (depth != 0)
2879                                 sgmlError(par, 0,
2880                                           _("Error: Wrong depth for LatexType Command.\n"));
2881
2882                         command_name = style->latexname();
2883
2884                         sgmlparam = style->latexparam();
2885                         c_params = split(sgmlparam, c_depth,'|');
2886
2887                         cmd_depth = lyx::atoi(c_depth);
2888
2889                         if (command_flag) {
2890                                 if (cmd_depth < command_base) {
2891                                         for (Paragraph::depth_type j = command_depth;
2892                                              j >= command_base; --j) {
2893                                                 sgml::closeTag(ofs, j, false, command_stack[j]);
2894                                                 ofs << endl;
2895                                         }
2896                                         command_depth = command_base = cmd_depth;
2897                                 } else if (cmd_depth <= command_depth) {
2898                                         for (int j = command_depth;
2899                                              j >= int(cmd_depth); --j) {
2900                                                 sgml::closeTag(ofs, j, false, command_stack[j]);
2901                                                 ofs << endl;
2902                                         }
2903                                         command_depth = cmd_depth;
2904                                 } else
2905                                         command_depth = cmd_depth;
2906                         } else {
2907                                 command_depth = command_base = cmd_depth;
2908                                 command_flag = true;
2909                         }
2910                         if (command_stack.size() == command_depth + 1)
2911                                 command_stack.push_back(string());
2912                         command_stack[command_depth] = command_name;
2913
2914                         // treat label as a special case for
2915                         // more WYSIWYM handling.
2916                         // This is a hack while paragraphs can't have
2917                         // attributes, like id in this case.
2918                         if (par->isInset(0)) {
2919                                 Inset * inset = par->getInset(0);
2920                                 Inset::Code lyx_code = inset->lyxCode();
2921                                 if (lyx_code == Inset::LABEL_CODE) {
2922                                         command_name += " id=\"";
2923                                         command_name += (static_cast<InsetCommand *>(inset))->getContents();
2924                                         command_name += '"';
2925                                         desc_on = 3;
2926                                 }
2927                         }
2928
2929                         sgml::openTag(ofs, depth + command_depth, false, command_name);
2930
2931                         item_name = c_params.empty() ? "title" : c_params;
2932                         sgml::openTag(ofs, depth + 1 + command_depth, false, item_name);
2933                         break;
2934
2935                 case LATEX_ENVIRONMENT:
2936                 case LATEX_ITEM_ENVIRONMENT:
2937                         if (depth < par->params().depth()) {
2938                                 depth = par->params().depth();
2939                                 environment_stack[depth].erase();
2940                         }
2941
2942                         if (environment_stack[depth] != style->latexname()) {
2943                                 if (environment_stack.size() == depth + 1) {
2944                                         environment_stack.push_back("!-- --");
2945                                         environment_inner.push_back("!-- --");
2946                                 }
2947                                 environment_stack[depth] = style->latexname();
2948                                 environment_inner[depth] = "!-- --";
2949                                 sgml::openTag(ofs, depth + command_depth, false, environment_stack[depth]);
2950                         } else {
2951                                 if (environment_inner[depth] != "!-- --") {
2952                                         item_name= "listitem";
2953                                         sgml::closeTag(ofs, command_depth + depth, false, item_name);
2954                                         if (environment_inner[depth] == "varlistentry")
2955                                                 sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
2956                                 }
2957                         }
2958
2959                         if (style->latextype == LATEX_ENVIRONMENT) {
2960                                 if (!style->latexparam().empty()) {
2961                                         if (style->latexparam() == "CDATA")
2962                                                 ofs << "<![CDATA[";
2963                                         else
2964                                                 sgml::openTag(ofs, depth + command_depth, false, style->latexparam());
2965                                 }
2966                                 break;
2967                         }
2968
2969                         desc_on = (style->labeltype == LABEL_MANUAL);
2970
2971                         environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
2972                         sgml::openTag(ofs, depth + 1 + command_depth,
2973                                     false, environment_inner[depth]);
2974
2975                         item_name = desc_on ? "term" : "para";
2976                         sgml::openTag(ofs, depth + 1 + command_depth,
2977                                     false, item_name);
2978                         break;
2979                 default:
2980                         sgml::openTag(ofs, depth + command_depth,
2981                                     false, style->latexname());
2982                         break;
2983                 }
2984
2985                 simpleDocBookOnePar(ofs, par, desc_on,
2986                                     depth + 1 + command_depth);
2987                 par = par->next();
2988
2989                 string end_tag;
2990                 // write closing SGML tags
2991                 switch (style->latextype) {
2992                 case LATEX_COMMAND:
2993                         end_tag = c_params.empty() ? "title" : c_params;
2994                         sgml::closeTag(ofs, depth + command_depth,
2995                                      false, end_tag);
2996                         break;
2997                 case LATEX_ENVIRONMENT:
2998                         if (!style->latexparam().empty()) {
2999                                 if (style->latexparam() == "CDATA")
3000                                         ofs << "]]>";
3001                                 else
3002                                         sgml::closeTag(ofs, depth + command_depth, false, style->latexparam());
3003                         }
3004                         break;
3005                 case LATEX_ITEM_ENVIRONMENT:
3006                         if (desc_on == 1) break;
3007                         end_tag = "para";
3008                         sgml::closeTag(ofs, depth + 1 + command_depth, false, end_tag);
3009                         break;
3010                 case LATEX_PARAGRAPH:
3011                         sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
3012                         break;
3013                 default:
3014                         sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
3015                         break;
3016                 }
3017         }
3018
3019         // Close open tags
3020         for (int d = depth; d >= 0; --d) {
3021                 if (!environment_stack[depth].empty()) {
3022                         if (environment_inner[depth] != "!-- --") {
3023                                 item_name = "listitem";
3024                                 sgml::closeTag(ofs, command_depth + depth, false, item_name);
3025                                if (environment_inner[depth] == "varlistentry")
3026                                        sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
3027                         }
3028
3029                         sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
3030                 }
3031         }
3032
3033         for (int j = command_depth; j >= 0 ; --j)
3034                 if (!command_stack[j].empty()) {
3035                         sgml::closeTag(ofs, j, false, command_stack[j]);
3036                         ofs << endl;
3037                 }
3038
3039         ofs << "\n\n";
3040         sgml::closeTag(ofs, 0, false, top_element);
3041
3042         ofs.close();
3043         // How to check for successful close
3044
3045         // we want this to be true outside previews (for insetexternal)
3046         niceFile = true;
3047 }
3048
3049
3050 void Buffer::simpleDocBookOnePar(ostream & os,
3051                                  Paragraph * par, int & desc_on,
3052                                  Paragraph::depth_type depth) const
3053 {
3054         bool emph_flag = false;
3055
3056         LyXLayout_ptr const & style = par->layout();
3057
3058         LyXFont font_old = (style->labeltype == LABEL_MANUAL ? style->labelfont : style->font);
3059
3060         int char_line_count = depth;
3061         //if (!style.free_spacing)
3062         //      os << string(depth,' ');
3063
3064         // parsing main loop
3065         for (pos_type i = 0; i < par->size(); ++i) {
3066                 LyXFont font = par->getFont(params, i);
3067
3068                 // handle <emphasis> tag
3069                 if (font_old.emph() != font.emph()) {
3070                         if (font.emph() == LyXFont::ON) {
3071                                 if (style->latexparam() == "CDATA")
3072                                         os << "]]>";
3073                                 os << "<emphasis>";
3074                                 if (style->latexparam() == "CDATA")
3075                                         os << "<![CDATA[";
3076                                 emph_flag = true;
3077                         } else if (i) {
3078                                 if (style->latexparam() == "CDATA")
3079                                         os << "]]>";
3080                                 os << "</emphasis>";
3081                                 if (style->latexparam() == "CDATA")
3082                                         os << "<![CDATA[";
3083                                 emph_flag = false;
3084                         }
3085                 }
3086
3087
3088                 if (par->isInset(i)) {
3089                         Inset * inset = par->getInset(i);
3090                         // don't print the inset in position 0 if desc_on == 3 (label)
3091                         if (i || desc_on != 3) {
3092                                 if (style->latexparam() == "CDATA")
3093                                         os << "]]>";
3094                                 inset->docbook(this, os, false);
3095                                 if (style->latexparam() == "CDATA")
3096                                         os << "<![CDATA[";
3097                         }
3098                 } else {
3099                         char c = par->getChar(i);
3100                         bool ws;
3101                         string str;
3102                         boost::tie(ws, str) = sgml::escapeChar(c);
3103
3104                         if (style->pass_thru) {
3105                                 os << c;
3106                         } else if (style->free_spacing || par->isFreeSpacing() || c != ' ') {
3107                                         os << str;
3108                         } else if (desc_on ==1) {
3109                                 ++char_line_count;
3110                                 os << "\n</term><listitem><para>";
3111                                 desc_on = 2;
3112                         } else {
3113                                 os << ' ';
3114                         }
3115                 }
3116                 font_old = font;
3117         }
3118
3119         if (emph_flag) {
3120                 if (style->latexparam() == "CDATA")
3121                         os << "]]>";
3122                 os << "</emphasis>";
3123                 if (style->latexparam() == "CDATA")
3124                         os << "<![CDATA[";
3125         }
3126
3127         // resets description flag correctly
3128         if (desc_on == 1) {
3129                 // <term> not closed...
3130                 os << "</term>\n<listitem><para>&nbsp;</para>";
3131         }
3132         if (style->free_spacing)
3133                 os << '\n';
3134 }
3135
3136
3137 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
3138 // Other flags: -wall -v0 -x
3139 int Buffer::runChktex()
3140 {
3141         if (!users->text) return 0;
3142
3143         users->owner()->prohibitInput();
3144
3145         // get LaTeX-Filename
3146         string const name = getLatexName();
3147         string path = filePath();
3148
3149         string const org_path = path;
3150         if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
3151                 path = tmppath;
3152         }
3153
3154         Path p(path); // path to LaTeX file
3155         users->owner()->message(_("Running chktex..."));
3156
3157         // Remove all error insets
3158         bool const removedErrorInsets = users->removeAutoInsets();
3159
3160         // Generate the LaTeX file if neccessary
3161         makeLaTeXFile(name, org_path, false);
3162
3163         TeXErrors terr;
3164         Chktex chktex(lyxrc.chktex_command, name, filePath());
3165         int res = chktex.run(terr); // run chktex
3166
3167         if (res == -1) {
3168                 Alert::alert(_("chktex did not work!"),
3169                            _("Could not run with file:"), name);
3170         } else if (res > 0) {
3171                 // Insert all errors as errors boxes
3172                 users->insertErrors(terr);
3173         }
3174
3175         // if we removed error insets before we ran chktex or if we inserted
3176         // error insets after we ran chktex, this must be run:
3177         if (removedErrorInsets || res) {
3178 #warning repaint needed here, or do you mean update() ?
3179                 users->repaint();
3180                 users->fitCursor();
3181         }
3182         users->owner()->allowInput();
3183
3184         return res;
3185 }
3186
3187
3188 void Buffer::validate(LaTeXFeatures & features) const
3189 {
3190         LyXTextClass const & tclass = params.getLyXTextClass();
3191
3192         if (params.tracking_changes) {
3193                 features.require("dvipost");
3194                 features.require("color");
3195         }
3196  
3197         // AMS Style is at document level
3198         if (params.use_amsmath || tclass.provides(LyXTextClass::amsmath))
3199                 features.require("amsmath");
3200
3201         for_each(paragraphs.begin(), paragraphs.end(),
3202                  boost::bind(&Paragraph::validate, _1, boost::ref(features)));
3203
3204         // the bullet shapes are buffer level not paragraph level
3205         // so they are tested here
3206         for (int i = 0; i < 4; ++i) {
3207                 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
3208                         int const font = params.user_defined_bullets[i].getFont();
3209                         if (font == 0) {
3210                                 int const c = params
3211                                         .user_defined_bullets[i]
3212                                         .getCharacter();
3213                                 if (c == 16
3214                                    || c == 17
3215                                    || c == 25
3216                                    || c == 26
3217                                    || c == 31) {
3218                                         features.require("latexsym");
3219                                 }
3220                         } else if (font == 1) {
3221                                 features.require("amssymb");
3222                         } else if ((font >= 2 && font <= 5)) {
3223                                 features.require("pifont");
3224                         }
3225                 }
3226         }
3227
3228         if (lyxerr.debugging(Debug::LATEX)) {
3229                 features.showStruct();
3230         }
3231 }
3232
3233
3234 vector<string> const Buffer::getLabelList() const
3235 {
3236         /// if this is a child document and the parent is already loaded
3237         /// Use the parent's list instead  [ale990407]
3238         if (!params.parentname.empty()
3239             && bufferlist.exists(params.parentname)) {
3240                 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
3241                 if (tmp)
3242                         return tmp->getLabelList();
3243         }
3244
3245         vector<string> label_list;
3246         for (inset_iterator it = inset_const_iterator_begin();
3247              it != inset_const_iterator_end(); ++it) {
3248                 vector<string> const l = it->getLabelList();
3249                 label_list.insert(label_list.end(), l.begin(), l.end());
3250         }
3251         return label_list;
3252 }
3253
3254
3255 // This is also a buffer property (ale)
3256 vector<pair<string, string> > const Buffer::getBibkeyList() const
3257 {
3258         typedef pair<string, string> StringPair;
3259         /// if this is a child document and the parent is already loaded
3260         /// Use the parent's list instead  [ale990412]
3261         if (!params.parentname.empty() && bufferlist.exists(params.parentname)) {
3262                 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
3263                 if (tmp)
3264                         return tmp->getBibkeyList();
3265         }
3266
3267         vector<StringPair> keys;
3268         ParagraphList::iterator pit = paragraphs.begin();
3269         ParagraphList::iterator pend = paragraphs.end();
3270         for (; pit != pend; ++pit) {
3271                 if (pit->bibkey) {
3272                         string const key = pit->bibkey->getContents();
3273                         string const opt = pit->bibkey->getOptions();
3274                         string const ref = pit->asString(this, false);
3275                         string const info = opt + "TheBibliographyRef" + ref;
3276
3277                         keys.push_back(StringPair(key, info));
3278                 }
3279         }
3280
3281         if (!keys.empty())
3282                 return keys;
3283
3284         // Might be either using bibtex or a child has bibliography
3285         for (inset_iterator it = inset_const_iterator_begin();
3286                 it != inset_const_iterator_end(); ++it) {
3287                 // Search for Bibtex or Include inset
3288                 if (it->lyxCode() == Inset::BIBTEX_CODE) {
3289                         vector<StringPair> tmp =
3290                                 static_cast<InsetBibtex &>(*it).getKeys(this);
3291                         keys.insert(keys.end(), tmp.begin(), tmp.end());
3292                 } else if (it->lyxCode() == Inset::INCLUDE_CODE) {
3293                         vector<StringPair> const tmp =
3294                                 static_cast<InsetInclude &>(*it).getKeys();
3295                         keys.insert(keys.end(), tmp.begin(), tmp.end());
3296                 }
3297         }
3298
3299         return keys;
3300 }
3301
3302
3303 bool Buffer::isDepClean(string const & name) const
3304 {
3305         DepClean::const_iterator it = dep_clean_.find(name);
3306         if (it == dep_clean_.end())
3307                 return true;
3308         return it->second;
3309 }
3310
3311
3312 void Buffer::markDepClean(string const & name)
3313 {
3314         dep_clean_[name] = true;
3315 }
3316
3317
3318 bool Buffer::dispatch(string const & command, bool * result)
3319 {
3320         // Split command string into command and argument
3321         string cmd;
3322         string line = ltrim(command);
3323         string const arg = trim(split(line, cmd, ' '));
3324
3325         return dispatch(lyxaction.LookupFunc(cmd), arg, result);
3326 }
3327
3328
3329 bool Buffer::dispatch(int action, string const & argument, bool * result)
3330 {
3331         bool dispatched = true;
3332
3333         switch (action) {
3334                 case LFUN_EXPORT: {
3335                         bool const tmp = Exporter::Export(this, argument, false);
3336                         if (result)
3337                                 *result = tmp;
3338                         break;
3339                 }
3340
3341                 default:
3342                         dispatched = false;
3343         }
3344         return dispatched;
3345 }
3346
3347
3348 void Buffer::resizeInsets(BufferView * bv)
3349 {
3350         /// then remove all LyXText in text-insets
3351         for_each(paragraphs.begin(), paragraphs.end(),
3352                  boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
3353 }
3354
3355
3356 void Buffer::redraw()
3357 {
3358 #warning repaint needed here, or do you mean update() ?
3359         users->repaint();
3360         users->fitCursor();
3361 }
3362
3363
3364 void Buffer::changeLanguage(Language const * from, Language const * to)
3365 {
3366
3367         ParIterator end = par_iterator_end();
3368         for (ParIterator it = par_iterator_begin(); it != end; ++it)
3369                 (*it)->changeLanguage(params, from, to);
3370 }
3371
3372
3373 bool Buffer::isMultiLingual()
3374 {
3375         ParIterator end = par_iterator_end();
3376         for (ParIterator it = par_iterator_begin(); it != end; ++it)
3377                 if ((*it)->isMultiLingual(params))
3378                         return true;
3379
3380         return false;
3381 }
3382
3383
3384 void Buffer::inset_iterator::setParagraph()
3385 {
3386         while (pit != pend) {
3387                 it = pit->insetlist.begin();
3388                 if (it != pit->insetlist.end())
3389                         return;
3390                 ++pit;
3391         }
3392 }
3393
3394
3395 Inset * Buffer::getInsetFromID(int id_arg) const
3396 {
3397         for (inset_iterator it = inset_const_iterator_begin();
3398                  it != inset_const_iterator_end(); ++it)
3399         {
3400                 if (it->id() == id_arg)
3401                         return &(*it);
3402                 Inset * in = it->getInsetFromID(id_arg);
3403                 if (in)
3404                         return in;
3405         }
3406         return 0;
3407 }
3408
3409
3410 Paragraph * Buffer::getParFromID(int id) const
3411 {
3412         if (id < 0)
3413                 return 0;
3414
3415         ParagraphList::iterator it = paragraphs.begin();
3416         ParagraphList::iterator end = paragraphs.end();
3417         for (; it != end; ++it) {
3418                 if (it->id() == id) {
3419                         return &*it;
3420                 }
3421                 Paragraph * tmp = it->getParFromID(id);
3422                 if (tmp) {
3423                         return tmp;
3424                 }
3425         }
3426         return 0;
3427 }
3428
3429
3430 ParIterator Buffer::par_iterator_begin()
3431 {
3432         return ParIterator(&*(paragraphs.begin()));
3433 }
3434
3435
3436 ParIterator Buffer::par_iterator_end()
3437 {
3438         return ParIterator();
3439 }
3440
3441 ParConstIterator Buffer::par_iterator_begin() const
3442 {
3443         return ParConstIterator(&*(paragraphs.begin()));
3444 }
3445
3446
3447 ParConstIterator Buffer::par_iterator_end() const
3448 {
3449         return ParConstIterator();
3450 }
3451
3452
3453
3454 void Buffer::addUser(BufferView * u)
3455 {
3456         users = u;
3457 }
3458
3459
3460 void Buffer::delUser(BufferView *)
3461 {
3462         users = 0;
3463 }
3464
3465
3466 Language const * Buffer::getLanguage() const
3467 {
3468         return params.language;
3469 }
3470
3471
3472 bool Buffer::isClean() const
3473 {
3474         return lyx_clean;
3475 }
3476
3477
3478 bool Buffer::isBakClean() const
3479 {
3480         return bak_clean;
3481 }
3482
3483
3484 void Buffer::markClean() const
3485 {
3486         if (!lyx_clean) {
3487                 lyx_clean = true;
3488                 updateTitles();
3489         }
3490         // if the .lyx file has been saved, we don't need an
3491         // autosave
3492         bak_clean = true;
3493 }
3494
3495
3496 void Buffer::markBakClean()
3497 {
3498         bak_clean = true;
3499 }
3500
3501
3502 void Buffer::setUnnamed(bool flag)
3503 {
3504         unnamed = flag;
3505 }
3506
3507
3508 bool Buffer::isUnnamed()
3509 {
3510         return unnamed;
3511 }
3512
3513
3514 void Buffer::markDirty()
3515 {
3516         if (lyx_clean) {
3517                 lyx_clean = false;
3518                 updateTitles();
3519         }
3520         bak_clean = false;
3521
3522         DepClean::iterator it = dep_clean_.begin();
3523         DepClean::const_iterator const end = dep_clean_.end();
3524
3525         for (; it != end; ++it) {
3526                 it->second = false;
3527         }
3528 }
3529
3530
3531 string const & Buffer::fileName() const
3532 {
3533         return filename_;
3534 }
3535
3536
3537 string const & Buffer::filePath() const
3538 {
3539         return filepath_;
3540 }
3541
3542
3543 bool Buffer::isReadonly() const
3544 {
3545         return read_only;
3546 }
3547
3548
3549 BufferView * Buffer::getUser() const
3550 {
3551         return users;
3552 }
3553
3554
3555 void Buffer::setParentName(string const & name)
3556 {
3557         params.parentname = name;
3558 }
3559
3560
3561 Buffer::inset_iterator::inset_iterator()
3562         : pit(0), pend(0)
3563 {}
3564
3565
3566 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
3567         : pit(p), pend(e)
3568 {
3569         setParagraph();
3570 }
3571
3572
3573 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
3574 {
3575         if (pit != pend) {
3576                 ++it;
3577                 if (it == pit->insetlist.end()) {
3578                         ++pit;
3579                         setParagraph();
3580                 }
3581         }
3582         return *this;
3583 }
3584
3585
3586 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
3587 {
3588         inset_iterator tmp = *this;
3589         ++*this;
3590         return tmp;
3591 }
3592
3593
3594 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
3595 {
3596         return *it.getInset();
3597 }
3598
3599
3600 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
3601 {
3602         return it.getInset();
3603 }
3604
3605
3606 Paragraph * Buffer::inset_iterator::getPar()
3607 {
3608         return &(*pit);
3609 }
3610
3611
3612 lyx::pos_type Buffer::inset_iterator::getPos() const
3613 {
3614         return it.getPos();
3615 }
3616
3617
3618 bool operator==(Buffer::inset_iterator const & iter1,
3619                 Buffer::inset_iterator const & iter2)
3620 {
3621         return iter1.pit == iter2.pit
3622                 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
3623 }
3624
3625
3626 bool operator!=(Buffer::inset_iterator const & iter1,
3627                 Buffer::inset_iterator const & iter2)
3628 {
3629         return !(iter1 == iter2);
3630 }