1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * This file is Copyright 1996-2001
12 * ======================================================
18 #include "bufferlist.h"
19 #include "LyXAction.h"
22 #include "tex-strings.h"
24 #include "bufferview_funcs.h"
30 #include "LaTeXFeatures.h"
37 #include "converter.h"
38 #include "BufferView.h"
39 #include "ParagraphParameters.h"
40 #include "iterators.h"
41 #include "lyxtextclasslist.h"
43 #include "paragraph_funcs.h"
46 #include "frontends/LyXView.h"
48 #include "mathed/formulamacro.h"
49 #include "mathed/formula.h"
51 #include "insets/inset.h"
52 #include "insets/inseterror.h"
53 #include "insets/insetlabel.h"
54 #include "insets/insetref.h"
55 #include "insets/inseturl.h"
56 #include "insets/insetnote.h"
57 #include "insets/insetquotes.h"
58 #include "insets/insetlatexaccent.h"
59 #include "insets/insetbib.h"
60 #include "insets/insetcite.h"
61 #include "insets/insetexternal.h"
62 #include "insets/insetindex.h"
63 #include "insets/insetinclude.h"
64 #include "insets/insettoc.h"
65 #include "insets/insetparent.h"
66 #include "insets/insetspecialchar.h"
67 #include "insets/insettext.h"
68 #include "insets/insetert.h"
69 #include "insets/insetgraphics.h"
70 #include "insets/insetfoot.h"
71 #include "insets/insetmarginal.h"
72 #include "insets/insetoptarg.h"
73 #include "insets/insetminipage.h"
74 #include "insets/insetfloat.h"
75 #include "insets/insetwrap.h"
76 #include "insets/insettabular.h"
78 #include "insets/insettheorem.h"
79 #include "insets/insetlist.h"
81 #include "insets/insetcaption.h"
82 #include "insets/insetfloatlist.h"
84 #include "frontends/Dialogs.h"
85 #include "frontends/Alert.h"
87 #include "graphics/Previews.h"
89 #include "support/textutils.h"
90 #include "support/filetools.h"
91 #include "support/path.h"
92 #include "support/os.h"
93 #include "support/lyxlib.h"
94 #include "support/FileInfo.h"
95 #include "support/lyxmanip.h"
96 #include "support/lyxalgo.h" // for lyx::count
97 #include "support/lyxtime.h"
99 #include <boost/bind.hpp>
100 #include <boost/tuple/tuple.hpp>
101 #include "BoostFormat.h"
113 #include <sys/types.h>
120 #ifndef CXX_GLOBAL_CSTD
132 using std::make_pair;
142 using lyx::textclass_type;
144 // all these externs should eventually be removed.
145 extern BufferList bufferlist;
149 const int LYX_FORMAT = 222;
153 Buffer::Buffer(string const & file, bool ronly)
154 : niceFile(true), lyx_clean(true), bak_clean(true),
155 unnamed(false), read_only(ronly),
156 filename_(file), users(0)
158 lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
159 filepath_ = OnlyPath(file);
161 if (read_only || lyxrc.use_tempdir) {
162 tmppath = CreateBufferTmpDir();
167 // set initial author
168 authorlist.record(Author(lyxrc.user_name, lyxrc.user_email));
174 lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
175 // here the buffer should take care that it is
176 // saved properly, before it goes into the void.
178 // make sure that views using this buffer
183 if (!tmppath.empty()) {
184 DestroyBufferTmpDir(tmppath);
189 // Remove any previewed LaTeX snippets assocoated with this buffer.
190 grfx::Previews::get().removeLoader(this);
194 string const Buffer::getLatexName(bool no_path) const
196 string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
198 return OnlyFilename(name);
204 pair<Buffer::LogType, string> const Buffer::getLogName() const
206 string const filename = getLatexName(false);
208 if (filename.empty())
209 return make_pair(Buffer::latexlog, string());
211 string path = OnlyPath(filename);
213 if (lyxrc.use_tempdir || !IsDirWriteable(path))
216 string const fname = AddName(path,
217 OnlyFilename(ChangeExtension(filename,
220 AddName(path, OnlyFilename(
221 ChangeExtension(filename,
222 formats.extension("literate") + ".out")));
224 // If no Latex log or Build log is newer, show Build log
226 FileInfo const f_fi(fname);
227 FileInfo const b_fi(bname);
230 (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
231 lyxerr[Debug::FILES] << "Log name calculated as: " << bname << endl;
232 return make_pair(Buffer::buildlog, bname);
234 lyxerr[Debug::FILES] << "Log name calculated as: " << fname << endl;
235 return make_pair(Buffer::latexlog, fname);
239 void Buffer::setReadonly(bool flag)
241 if (read_only != flag) {
244 users->owner()->getDialogs().updateBufferDependent(false);
249 AuthorList & Buffer::authors()
255 /// Update window titles of all users
256 // Should work on a list
257 void Buffer::updateTitles() const
260 users->owner()->updateWindowTitle();
264 /// Reset autosave timer of all users
265 // Should work on a list
266 void Buffer::resetAutosaveTimers() const
269 users->owner()->resetAutosaveTimer();
273 void Buffer::setFileName(string const & newfile)
275 filename_ = MakeAbsPath(newfile);
276 filepath_ = OnlyPath(filename_);
277 setReadonly(IsFileWriteable(filename_) == 0);
282 // We'll remove this later. (Lgb)
285 string last_inset_read;
288 #warning And _why_ is this here? (Lgb)
292 vector<int> author_ids;
297 // candidate for move to BufferView
298 // (at least some parts in the beginning of the func)
301 // changed to be public and have one parameter
302 // if par = 0 normal behavior
303 // else insert behavior
304 // Returns false if "\the_end" is not read (Asger)
305 bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
312 Paragraph::depth_type depth = 0;
313 bool the_end_read = false;
315 Paragraph * first_par = 0;
316 LyXFont font(LyXFont::ALL_INHERIT, params.language);
319 if (file_format < 216 && params.language->lang() == "hebrew")
320 font.setLanguage(default_language);
325 par->layout(params.getLyXTextClass().defaultLayout());
327 // We are inserting into an existing document
328 users->text->breakParagraph(users);
329 first_par = users->text->ownerParagraph();
332 // We don't want to adopt the parameters from the
333 // document we insert, so we skip until the text begins:
336 string const pretoken = lex.getString();
337 if (pretoken == "\\layout") {
338 lex.pushToken(pretoken);
346 string const token = lex.getString();
348 if (token.empty()) continue;
350 lyxerr[Debug::PARSER] << "Handling token: `"
351 << token << '\'' << endl;
354 parseSingleLyXformat2Token(lex, par, first_par,
362 paragraphs.set(first_par);
364 if (unknown_layouts > 0) {
365 string s = _("Couldn't set the layout for ");
366 if (unknown_layouts == 1) {
367 s += _("one paragraph");
369 s += tostr(unknown_layouts);
370 s += _(" paragraphs");
373 Alert::alert(_("Textclass Loading Error!"), s,
374 boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
376 Alert::alert(_("Textclass Loading Error!"), s,
377 _("When reading ") + fileName());
381 if (unknown_tokens > 0) {
382 string s = _("Encountered ");
383 if (unknown_tokens == 1) {
384 s += _("one unknown token");
386 s += tostr(unknown_tokens);
387 s += _(" unknown tokens");
390 Alert::alert(_("Textclass Loading Error!"), s,
391 boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
393 Alert::alert(_("Textclass Loading Error!"), s,
394 _("When reading ") + fileName());
403 // This stuff is, in the traditional LyX terminology, Super UGLY
404 // but this code is too b0rken to admit of a better solution yet
405 Change current_change;
410 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
411 Paragraph *& first_par,
412 string const & token, int & pos,
413 Paragraph::depth_type & depth,
417 bool the_end_read = false;
419 // The order of the tags tested may seem unnatural, but this
420 // has been done in order to reduce the number of string
421 // comparisons needed to recognize a given token. This leads
422 // on large documents like UserGuide to a reduction of a
424 if (token[0] != '\\') {
425 for (string::const_iterator cit = token.begin();
426 cit != token.end(); ++cit) {
427 par->insertChar(pos, (*cit), font, current_change);
430 } else if (token == "\\layout") {
431 // reset the font as we start a new layout and if the font is
432 // not ALL_INHERIT,document_language then it will be set to the
433 // right values after this tag (Jug 20020420)
434 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
437 if (file_format < 216 && params.language->lang() == "hebrew")
438 font.setLanguage(default_language);
442 string layoutname = lex.getString();
444 LyXTextClass const & tclass = params.getLyXTextClass();
446 if (layoutname.empty()) {
447 layoutname = tclass.defaultLayoutName();
449 bool hasLayout = tclass.hasLayout(layoutname);
451 lyxerr << "Layout '" << layoutname << "' does not"
452 << " exist in textclass '" << tclass.name()
454 lyxerr << "Trying to use default layout instead."
456 layoutname = tclass.defaultLayoutName();
460 // The is the compability reading of layout caption.
461 // It can be removed in LyX version 1.3.0. (Lgb)
462 if (compare_ascii_no_case(layoutname, "caption") == 0) {
463 // We expect that the par we are now working on is
464 // really inside a InsetText inside a InsetFloat.
465 // We also know that captions can only be
466 // one paragraph. (Lgb)
468 // We should now read until the next "\layout"
470 // This is probably not good enough, what if the
471 // caption is the last par in the document (Lgb)
472 istream & ist = lex.getStream();
478 if (prefixIs(line, "\\layout")) {
482 if (prefixIs(line, "\\begin_inset"))
484 if (prefixIs(line, "\\end_inset")) {
495 // Now we should have the whole layout in ss
496 // we should now be able to give this to the
498 ss << "\\end_inset\n";
500 // This seems like a bug in stringstream.
501 // We really should be able to use ss
503 istringstream is(ss.str());
505 tmplex.setStream(is);
506 Inset * inset = new InsetCaption;
507 inset->Read(this, tmplex);
508 par->InsertInset(pos, inset, font);
515 par = new Paragraph(par);
516 par->layout(params.getLyXTextClass().defaultLayout());
517 if (params.tracking_changes)
521 par->layout(params.getLyXTextClass()[layoutname]);
522 // Test whether the layout is obsolete.
523 LyXLayout_ptr const & layout = par->layout();
524 if (!layout->obsoleted_by().empty())
525 par->layout(params.getLyXTextClass()[layout->obsoleted_by()]);
526 par->params().depth(depth);
531 } else if (token == "\\end_inset") {
532 lyxerr << "Solitary \\end_inset. Missing \\begin_inset?.\n"
533 << "Last inset read was: " << last_inset_read
535 // Simply ignore this. The insets do not have
537 // But insets should read it, it is a part of
538 // the inset isn't it? Lgb.
539 } else if (token == "\\begin_inset") {
540 readInset(lex, par, pos, font);
541 } else if (token == "\\family") {
543 font.setLyXFamily(lex.getString());
544 } else if (token == "\\series") {
546 font.setLyXSeries(lex.getString());
547 } else if (token == "\\shape") {
549 font.setLyXShape(lex.getString());
550 } else if (token == "\\size") {
552 font.setLyXSize(lex.getString());
553 } else if (token == "\\lang") {
555 string const tok = lex.getString();
556 Language const * lang = languages.getLanguage(tok);
558 font.setLanguage(lang);
560 font.setLanguage(params.language);
561 lex.printError("Unknown language `$$Token'");
563 } else if (token == "\\numeric") {
565 font.setNumber(font.setLyXMisc(lex.getString()));
566 } else if (token == "\\emph") {
568 font.setEmph(font.setLyXMisc(lex.getString()));
569 } else if (token == "\\bar") {
571 string const tok = lex.getString();
572 // This is dirty, but gone with LyX3. (Asger)
574 font.setUnderbar(LyXFont::ON);
575 else if (tok == "no")
576 font.setUnderbar(LyXFont::OFF);
577 else if (tok == "default")
578 font.setUnderbar(LyXFont::INHERIT);
580 lex.printError("Unknown bar font flag "
582 } else if (token == "\\noun") {
584 font.setNoun(font.setLyXMisc(lex.getString()));
585 } else if (token == "\\color") {
587 font.setLyXColor(lex.getString());
588 } else if (token == "\\SpecialChar") {
589 LyXLayout_ptr const & layout = par->layout();
591 // Insets don't make sense in a free-spacing context! ---Kayvan
592 if (layout->free_spacing || par->isFreeSpacing()) {
595 string const next_token = lex.getString();
596 if (next_token == "\\-") {
597 par->insertChar(pos, '-', font, current_change);
598 } else if (next_token == "~") {
599 par->insertChar(pos, ' ', font, current_change);
601 lex.printError("Token `$$Token' "
603 "paragraph layout!");
608 Inset * inset = new InsetSpecialChar;
609 inset->read(this, lex);
610 par->insertInset(pos, inset, font, current_change);
613 } else if (token == "\\i") {
614 Inset * inset = new InsetLatexAccent;
615 inset->read(this, lex);
616 par->insertInset(pos, inset, font, current_change);
618 } else if (token == "\\backslash") {
619 par->insertChar(pos, '\\', font, current_change);
621 } else if (token == "\\begin_deeper") {
623 } else if (token == "\\end_deeper") {
625 lex.printError("\\end_deeper: "
626 "depth is already null");
630 } else if (token == "\\begin_preamble") {
631 params.readPreamble(lex);
632 } else if (token == "\\textclass") {
634 pair<bool, textclass_type> pp =
635 textclasslist.NumberOfClass(lex.getString());
637 params.textclass = pp.second;
640 Alert::alert(_("Textclass error"),
641 boost::io::str(boost::format(_("The document uses an unknown textclass \"%1$s\".")) % lex.getString()),
642 _("-- substituting default."));
645 _("Textclass error"),
646 _("The document uses an unknown textclass ")
648 _("-- substituting default."));
650 params.textclass = 0;
652 if (!params.getLyXTextClass().load()) {
653 // if the textclass wasn't loaded properly
654 // we need to either substitute another
655 // or stop loading the file.
656 // I can substitute but I don't see how I can
657 // stop loading... ideas?? ARRae980418
659 Alert::alert(_("Textclass Loading Error!"),
660 boost::io::str(boost::format(_("Can't load textclass %1$s")) %
661 params.getLyXTextClass().name()),
662 _("-- substituting default."));
664 Alert::alert(_("Textclass Loading Error!"),
665 _("Can't load textclass ")
666 + params.getLyXTextClass().name(),
667 _("-- substituting default."));
669 params.textclass = 0;
671 } else if (token == "\\options") {
673 params.options = lex.getString();
674 } else if (token == "\\language") {
675 params.readLanguage(lex);
676 } else if (token == "\\fontencoding") {
678 } else if (token == "\\inputencoding") {
680 params.inputenc = lex.getString();
681 } else if (token == "\\graphics") {
682 params.readGraphicsDriver(lex);
683 } else if (token == "\\fontscheme") {
685 params.fonts = lex.getString();
686 } else if (token == "\\noindent") {
687 par->params().noindent(true);
688 } else if (token == "\\leftindent") {
690 LyXLength value(lex.getString());
691 par->params().leftIndent(value);
692 } else if (token == "\\fill_top") {
693 par->params().spaceTop(VSpace(VSpace::VFILL));
694 } else if (token == "\\fill_bottom") {
695 par->params().spaceBottom(VSpace(VSpace::VFILL));
696 } else if (token == "\\line_top") {
697 par->params().lineTop(true);
698 } else if (token == "\\line_bottom") {
699 par->params().lineBottom(true);
700 } else if (token == "\\pagebreak_top") {
701 par->params().pagebreakTop(true);
702 } else if (token == "\\pagebreak_bottom") {
703 par->params().pagebreakBottom(true);
704 } else if (token == "\\start_of_appendix") {
705 par->params().startOfAppendix(true);
706 } else if (token == "\\paragraph_separation") {
707 int tmpret = lex.findToken(string_paragraph_separation);
710 params.paragraph_separation =
711 static_cast<BufferParams::PARSEP>(tmpret);
712 } else if (token == "\\defskip") {
714 params.defskip = VSpace(lex.getString());
715 } else if (token == "\\quotes_language") {
716 int tmpret = lex.findToken(string_quotes_language);
719 InsetQuotes::quote_language tmpl =
720 InsetQuotes::EnglishQ;
723 tmpl = InsetQuotes::EnglishQ;
726 tmpl = InsetQuotes::SwedishQ;
729 tmpl = InsetQuotes::GermanQ;
732 tmpl = InsetQuotes::PolishQ;
735 tmpl = InsetQuotes::FrenchQ;
738 tmpl = InsetQuotes::DanishQ;
741 params.quotes_language = tmpl;
742 } else if (token == "\\quotes_times") {
744 switch (lex.getInteger()) {
746 params.quotes_times = InsetQuotes::SingleQ;
749 params.quotes_times = InsetQuotes::DoubleQ;
752 } else if (token == "\\papersize") {
753 int tmpret = lex.findToken(string_papersize);
757 params.papersize2 = tmpret;
758 } else if (token == "\\paperpackage") {
759 int tmpret = lex.findToken(string_paperpackages);
762 params.paperpackage = BufferParams::PACKAGE_NONE;
764 params.paperpackage = tmpret;
765 } else if (token == "\\use_geometry") {
767 params.use_geometry = lex.getInteger();
768 } else if (token == "\\use_amsmath") {
770 params.use_amsmath = lex.getInteger();
771 } else if (token == "\\use_natbib") {
773 params.use_natbib = lex.getInteger();
774 } else if (token == "\\use_numerical_citations") {
776 params.use_numerical_citations = lex.getInteger();
777 } else if (token == "\\tracking_changes") {
779 params.tracking_changes = lex.getInteger();
780 // mark the first paragraph
781 if (params.tracking_changes)
783 } else if (token == "\\author") {
785 istringstream ss(lex.getString());
788 int aid(authorlist.record(a));
789 lyxerr << "aid is " << aid << endl;
790 lyxerr << "listed aid is " << author_ids.size() << endl;
791 author_ids.push_back(authorlist.record(a));
792 } else if (token == "\\paperorientation") {
793 int tmpret = lex.findToken(string_orientation);
797 static_cast<BufferParams::PAPER_ORIENTATION>(tmpret);
798 } else if (token == "\\paperwidth") {
800 params.paperwidth = lex.getString();
801 } else if (token == "\\paperheight") {
803 params.paperheight = lex.getString();
804 } else if (token == "\\leftmargin") {
806 params.leftmargin = lex.getString();
807 } else if (token == "\\topmargin") {
809 params.topmargin = lex.getString();
810 } else if (token == "\\rightmargin") {
812 params.rightmargin = lex.getString();
813 } else if (token == "\\bottommargin") {
815 params.bottommargin = lex.getString();
816 } else if (token == "\\headheight") {
818 params.headheight = lex.getString();
819 } else if (token == "\\headsep") {
821 params.headsep = lex.getString();
822 } else if (token == "\\footskip") {
824 params.footskip = lex.getString();
825 } else if (token == "\\paperfontsize") {
827 params.fontsize = rtrim(lex.getString());
828 } else if (token == "\\papercolumns") {
830 params.columns = lex.getInteger();
831 } else if (token == "\\papersides") {
833 switch (lex.getInteger()) {
835 case 1: params.sides = LyXTextClass::OneSide; break;
836 case 2: params.sides = LyXTextClass::TwoSides; break;
838 } else if (token == "\\paperpagestyle") {
840 params.pagestyle = rtrim(lex.getString());
841 } else if (token == "\\bullet") {
843 int const index = lex.getInteger();
845 int temp_int = lex.getInteger();
846 params.user_defined_bullets[index].setFont(temp_int);
847 params.temp_bullets[index].setFont(temp_int);
849 temp_int = lex.getInteger();
850 params.user_defined_bullets[index].setCharacter(temp_int);
851 params.temp_bullets[index].setCharacter(temp_int);
853 temp_int = lex.getInteger();
854 params.user_defined_bullets[index].setSize(temp_int);
855 params.temp_bullets[index].setSize(temp_int);
857 string const temp_str = lex.getString();
858 if (temp_str != "\\end_bullet") {
859 // this element isn't really necessary for
860 // parsing but is easier for humans
861 // to understand bullets. Put it back and
862 // set a debug message?
863 lex.printError("\\end_bullet expected, got" + temp_str);
864 //how can I put it back?
866 } else if (token == "\\bulletLaTeX") {
867 // The bullet class should be able to read this.
869 int const index = lex.getInteger();
871 string temp_str = lex.getString();
873 while (temp_str != "\\end_bullet") {
874 // this loop structure is needed when user
875 // enters an empty string since the first
876 // thing returned will be the \\end_bullet
878 // if the LaTeX entry has spaces. Each element
879 // therefore needs to be read in turn
882 temp_str = lex.getString();
885 params.user_defined_bullets[index].setText(sum_str);
886 params.temp_bullets[index].setText(sum_str);
887 } else if (token == "\\secnumdepth") {
889 params.secnumdepth = lex.getInteger();
890 } else if (token == "\\tocdepth") {
892 params.tocdepth = lex.getInteger();
893 } else if (token == "\\spacing") {
895 string const tmp = rtrim(lex.getString());
896 Spacing::Space tmp_space = Spacing::Default;
898 if (tmp == "single") {
899 tmp_space = Spacing::Single;
900 } else if (tmp == "onehalf") {
901 tmp_space = Spacing::Onehalf;
902 } else if (tmp == "double") {
903 tmp_space = Spacing::Double;
904 } else if (tmp == "other") {
906 tmp_space = Spacing::Other;
907 tmp_val = lex.getFloat();
909 lex.printError("Unknown spacing token: '$$Token'");
911 // Small hack so that files written with klyx will be
914 par->params().spacing(Spacing(tmp_space, tmp_val));
916 params.spacing.set(tmp_space, tmp_val);
918 } else if (token == "\\paragraph_spacing") {
920 string const tmp = rtrim(lex.getString());
921 if (tmp == "single") {
922 par->params().spacing(Spacing(Spacing::Single));
923 } else if (tmp == "onehalf") {
924 par->params().spacing(Spacing(Spacing::Onehalf));
925 } else if (tmp == "double") {
926 par->params().spacing(Spacing(Spacing::Double));
927 } else if (tmp == "other") {
929 par->params().spacing(Spacing(Spacing::Other,
932 lex.printError("Unknown spacing token: '$$Token'");
934 } else if (token == "\\float_placement") {
936 params.float_placement = lex.getString();
937 } else if (token == "\\align") {
938 int tmpret = lex.findToken(string_align);
939 if (tmpret == -1) ++tmpret;
940 int const tmpret2 = int(pow(2.0, tmpret));
941 par->params().align(LyXAlignment(tmpret2));
942 } else if (token == "\\added_space_top") {
944 VSpace value = VSpace(lex.getString());
945 // only add the length when value > 0 or
947 if ((value.length().len().value() != 0) ||
949 (value.kind() != VSpace::LENGTH))
950 par->params().spaceTop(value);
951 } else if (token == "\\added_space_bottom") {
953 VSpace value = VSpace(lex.getString());
954 // only add the length when value > 0 or
956 if ((value.length().len().value() != 0) ||
958 (value.kind() != VSpace::LENGTH))
959 par->params().spaceBottom(value);
960 } else if (token == "\\labelwidthstring") {
962 par->params().labelWidthString(lex.getString());
963 // do not delete this token, it is still needed!
964 } else if (token == "\\newline") {
965 par->insertChar(pos, Paragraph::META_NEWLINE, font, current_change);
967 } else if (token == "\\LyXTable") {
968 Inset * inset = new InsetTabular(*this);
969 inset->read(this, lex);
970 par->insertInset(pos, inset, font, current_change);
972 } else if (token == "\\hfill") {
973 par->insertChar(pos, Paragraph::META_HFILL, font, current_change);
975 } else if (token == "\\change_unchanged") {
976 // Hack ! Needed for empty paragraphs :/
979 current_change = Change(Change::UNCHANGED);
980 } else if (token == "\\change_inserted") {
982 istringstream istr(lex.getString());
987 current_change = Change(Change::INSERTED, author_ids[aid], ct);
988 } else if (token == "\\change_deleted") {
990 istringstream istr(lex.getString());
995 current_change = Change(Change::DELETED, author_ids[aid], ct);
996 } else if (token == "\\bibitem") { // ale970302
998 InsetCommandParams p("bibitem", "dummy");
999 par->bibkey = new InsetBibKey(p);
1001 par->bibkey->read(this, lex);
1002 } else if (token == "\\the_end") {
1003 the_end_read = true;
1005 // This should be insurance for the future: (Asger)
1008 #if USE_BOOST_FORMAT
1009 boost::format fmt(_("Unknown token: %1$s %2$s\n"));
1010 fmt % token % lex.text();
1011 string const s = fmt.str();
1013 string const s = _("Unknown token: ") + token
1014 + ' ' + lex.text() + '\n';
1016 // we can do this here this way because we're actually reading
1017 // the buffer and don't care about LyXText right now.
1018 InsetError * new_inset = new InsetError(s);
1019 par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
1024 return the_end_read;
1028 // needed to insert the selection
1029 void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
1030 LyXFont const & fn,string const & str) const
1032 LyXLayout_ptr const & layout = par->layout();
1036 par->checkInsertChar(font);
1037 // insert the string, don't insert doublespace
1038 bool space_inserted = true;
1039 bool autobreakrows = !par->inInset() ||
1040 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
1041 for(string::const_iterator cit = str.begin();
1042 cit != str.end(); ++cit) {
1044 if (autobreakrows && (!par->empty() || layout->keepempty)) {
1045 breakParagraph(params, par, pos,
1046 layout->isEnvironment());
1049 space_inserted = true;
1053 // do not insert consecutive spaces if !free_spacing
1054 } else if ((*cit == ' ' || *cit == '\t') &&
1055 space_inserted && !layout->free_spacing &&
1056 !par->isFreeSpacing())
1059 } else if (*cit == '\t') {
1060 if (!layout->free_spacing && !par->isFreeSpacing()) {
1061 // tabs are like spaces here
1062 par->insertChar(pos, ' ', font, current_change);
1064 space_inserted = true;
1066 const pos_type nb = 8 - pos % 8;
1067 for (pos_type a = 0; a < nb ; ++a) {
1068 par->insertChar(pos, ' ', font, current_change);
1071 space_inserted = true;
1073 } else if (!IsPrintable(*cit)) {
1074 // Ignore unprintables
1077 // just insert the character
1078 par->insertChar(pos, *cit, font);
1080 space_inserted = (*cit == ' ');
1087 void Buffer::readInset(LyXLex & lex, Paragraph *& par,
1088 int & pos, LyXFont & font)
1090 // consistency check
1091 if (lex.getString() != "\\begin_inset") {
1092 lyxerr << "Buffer::readInset: Consistency check failed."
1099 string const tmptok = lex.getString();
1100 last_inset_read = tmptok;
1102 // test the different insets
1103 if (tmptok == "LatexCommand") {
1104 InsetCommandParams inscmd;
1107 string const cmdName = inscmd.getCmdName();
1109 // This strange command allows LyX to recognize "natbib" style
1110 // citations: citet, citep, Citet etc.
1111 if (compare_ascii_no_case(cmdName.substr(0,4), "cite") == 0) {
1112 inset = new InsetCitation(inscmd);
1113 } else if (cmdName == "bibitem") {
1114 lex.printError("Wrong place for bibitem");
1115 inset = new InsetBibKey(inscmd);
1116 } else if (cmdName == "BibTeX") {
1117 inset = new InsetBibtex(inscmd);
1118 } else if (cmdName == "index") {
1119 inset = new InsetIndex(inscmd);
1120 } else if (cmdName == "include") {
1121 inset = new InsetInclude(inscmd, *this);
1122 } else if (cmdName == "label") {
1123 inset = new InsetLabel(inscmd);
1124 } else if (cmdName == "url"
1125 || cmdName == "htmlurl") {
1126 inset = new InsetUrl(inscmd);
1127 } else if (cmdName == "ref"
1128 || cmdName == "pageref"
1129 || cmdName == "vref"
1130 || cmdName == "vpageref"
1131 || cmdName == "prettyref") {
1132 if (!inscmd.getOptions().empty()
1133 || !inscmd.getContents().empty()) {
1134 inset = new InsetRef(inscmd, *this);
1136 } else if (cmdName == "tableofcontents") {
1137 inset = new InsetTOC(inscmd);
1138 } else if (cmdName == "listofalgorithms") {
1139 inset = new InsetFloatList("algorithm");
1140 } else if (cmdName == "listoffigures") {
1141 inset = new InsetFloatList("figure");
1142 } else if (cmdName == "listoftables") {
1143 inset = new InsetFloatList("table");
1144 } else if (cmdName == "printindex") {
1145 inset = new InsetPrintIndex(inscmd);
1146 } else if (cmdName == "lyxparent") {
1147 inset = new InsetParent(inscmd, *this);
1150 bool alreadyread = false;
1151 if (tmptok == "Quotes") {
1152 inset = new InsetQuotes;
1153 } else if (tmptok == "External") {
1154 inset = new InsetExternal;
1155 } else if (tmptok == "FormulaMacro") {
1156 inset = new InsetFormulaMacro;
1157 } else if (tmptok == "Formula") {
1158 inset = new InsetFormula;
1159 } else if (tmptok == "Figure") { // Backward compatibility
1160 // inset = new InsetFig(100, 100, *this);
1161 inset = new InsetGraphics;
1162 } else if (tmptok == "Graphics") {
1163 inset = new InsetGraphics;
1164 } else if (tmptok == "Info") {// backwards compatibility
1165 inset = new InsetNote(this,
1166 lex.getLongString("\\end_inset"),
1169 } else if (tmptok == "Note") {
1170 inset = new InsetNote(params);
1171 } else if (tmptok == "Include") {
1172 InsetCommandParams p("Include");
1173 inset = new InsetInclude(p, *this);
1174 } else if (tmptok == "ERT") {
1175 inset = new InsetERT(params);
1176 } else if (tmptok == "Tabular") {
1177 inset = new InsetTabular(*this);
1178 } else if (tmptok == "Text") {
1179 inset = new InsetText(params);
1180 } else if (tmptok == "Foot") {
1181 inset = new InsetFoot(params);
1182 } else if (tmptok == "Marginal") {
1183 inset = new InsetMarginal(params);
1184 } else if (tmptok == "OptArg") {
1185 inset = new InsetOptArg(params);
1186 } else if (tmptok == "Minipage") {
1187 inset = new InsetMinipage(params);
1188 } else if (tmptok == "Float") {
1190 string tmptok = lex.getString();
1191 inset = new InsetFloat(params, tmptok);
1192 } else if (tmptok == "Wrap") {
1194 string tmptok = lex.getString();
1195 inset = new InsetWrap(params, tmptok);
1197 } else if (tmptok == "List") {
1198 inset = new InsetList;
1199 } else if (tmptok == "Theorem") {
1200 inset = new InsetList;
1202 } else if (tmptok == "Caption") {
1203 inset = new InsetCaption(params);
1204 } else if (tmptok == "FloatList") {
1205 inset = new InsetFloatList;
1208 if (inset && !alreadyread) inset->read(this, lex);
1212 par->insertInset(pos, inset, font, current_change);
1218 bool Buffer::readFile(LyXLex & lex, string const & filename, Paragraph * par)
1222 string const token(lex.getString());
1223 if (token == "\\lyxformat") { // the first token _must_ be...
1225 string tmp_format = lex.getString();
1226 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
1227 // if present remove ".," from string.
1228 string::size_type dot = tmp_format.find_first_of(".,");
1229 //lyxerr << " dot found at " << dot << endl;
1230 if (dot != string::npos)
1231 tmp_format.erase(dot, 1);
1232 file_format = strToInt(tmp_format);
1233 //lyxerr << "format: " << file_format << endl;
1234 if (file_format == LYX_FORMAT) {
1236 } else if (file_format > LYX_FORMAT) {
1238 Alert::alert(_("Warning!"),
1239 _("The file was created with a newer version of "
1240 "LyX. This is likely to cause problems."));
1242 } else if (file_format < LYX_FORMAT) {
1244 if (file_format < 200) {
1245 Alert::alert(_("ERROR!"),
1246 _("Old LyX file format found. "
1247 "Use LyX 0.10.x to read this!"));
1249 } else if (!filename.empty()) {
1251 LibFileSearch("lyx2lyx", "lyx2lyx");
1252 if (command.empty()) {
1253 Alert::alert(_("ERROR!"),
1254 _("Can't find conversion script."));
1258 +tostr(LYX_FORMAT) + ' '
1259 + QuoteName(filename);
1260 lyxerr[Debug::INFO] << "Running '"
1263 cmd_ret const ret = RunCommand(command);
1265 Alert::alert(_("ERROR!"),
1266 _("An error occured while "
1267 "running the conversion script."));
1270 istringstream is(STRCONV(ret.second));
1271 LyXLex tmplex(0, 0);
1272 tmplex.setStream(is);
1273 return readFile(tmplex, string(), par);
1275 // This code is reached if lyx2lyx failed (for
1276 // some reason) to change the file format of
1282 bool the_end = readLyXformat2(lex, par);
1283 params.setPaperStuff();
1286 // the_end was added in 213
1287 if (file_format < 213)
1292 Alert::alert(_("Warning!"),
1293 _("Reading of document is not complete"),
1294 _("Maybe the document is truncated"));
1297 } else { // "\\lyxformat" not found
1298 Alert::alert(_("ERROR!"), _("Not a LyX file!"));
1301 Alert::alert(_("ERROR!"), _("Unable to read file!"));
1306 // Should probably be moved to somewhere else: BufferView? LyXView?
1307 bool Buffer::save() const
1309 // We don't need autosaves in the immediate future. (Asger)
1310 resetAutosaveTimers();
1314 if (lyxrc.make_backup) {
1315 s = fileName() + '~';
1316 if (!lyxrc.backupdir_path.empty())
1317 s = AddName(lyxrc.backupdir_path,
1318 subst(os::slashify_path(s),'/','!'));
1320 // Rename is the wrong way of making a backup,
1321 // this is the correct way.
1322 /* truss cp fil fil2:
1323 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
1324 stat("LyXVC.lyx", 0xEFFFF688) = 0
1325 open("LyXVC.lyx", O_RDONLY) = 3
1326 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
1327 fstat(4, 0xEFFFF508) = 0
1328 fstat(3, 0xEFFFF508) = 0
1329 read(3, " # T h i s f i l e w".., 8192) = 5579
1330 write(4, " # T h i s f i l e w".., 5579) = 5579
1331 read(3, 0xEFFFD4A0, 8192) = 0
1334 chmod("LyXVC3.lyx", 0100644) = 0
1335 lseek(0, 0, SEEK_CUR) = 46440
1339 // Should probably have some more error checking here.
1340 // Doing it this way, also makes the inodes stay the same.
1341 // This is still not a very good solution, in particular we
1342 // might loose the owner of the backup.
1343 FileInfo finfo(fileName());
1344 if (finfo.exist()) {
1345 mode_t fmode = finfo.getMode();
1346 struct utimbuf times = {
1347 finfo.getAccessTime(),
1348 finfo.getModificationTime() };
1350 ifstream ifs(fileName().c_str());
1351 ofstream ofs(s.c_str(), ios::out|ios::trunc);
1356 ::chmod(s.c_str(), fmode);
1358 if (::utime(s.c_str(), ×)) {
1359 lyxerr << "utime error." << endl;
1362 lyxerr << "LyX was not able to make "
1363 "backup copy. Beware." << endl;
1368 if (writeFile(fileName())) {
1370 removeAutosaveFile(fileName());
1372 // Saving failed, so backup is not backup
1373 if (lyxrc.make_backup) {
1374 lyx::rename(s, fileName());
1382 bool Buffer::writeFile(string const & fname) const
1384 if (read_only && (fname == fileName())) {
1388 FileInfo finfo(fname);
1389 if (finfo.exist() && !finfo.writable()) {
1393 ofstream ofs(fname.c_str());
1399 // Use the standard "C" locale for file output.
1400 ofs.imbue(std::locale::classic());
1403 // The top of the file should not be written by params.
1405 // write out a comment in the top of the file
1406 ofs << '#' << lyx_docversion
1407 << " created this file. For more info see http://www.lyx.org/\n"
1408 << "\\lyxformat " << LYX_FORMAT << "\n";
1410 // now write out the buffer paramters.
1411 params.writeFile(ofs);
1413 // if we're tracking, list all possible authors
1414 if (params.tracking_changes) {
1415 AuthorList::Authors::const_iterator it = authorlist.begin();
1416 AuthorList::Authors::const_iterator end = authorlist.end();
1417 for (; it != end; ++it) {
1418 ofs << "\\author " << it->second << "\n";
1422 Paragraph::depth_type depth = 0;
1424 // this will write out all the paragraphs
1425 // using recursive descent.
1426 ParagraphList::iterator pit = paragraphs.begin();
1427 ParagraphList::iterator pend = paragraphs.end();
1428 for (; pit != pend; ++pit)
1429 pit->write(this, ofs, params, depth);
1431 // Write marker that shows file is complete
1432 ofs << "\n\\the_end" << endl;
1436 // how to check if close went ok?
1437 // Following is an attempt... (BE 20001011)
1439 // good() returns false if any error occured, including some
1440 // formatting error.
1441 // bad() returns true if something bad happened in the buffer,
1442 // which should include file system full errors.
1449 lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1451 lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1463 pair<int, string> const addDepth(int depth, int ldepth)
1467 d += (ldepth - depth) * 2;
1468 return make_pair(d, string(d, ' '));
1474 string const Buffer::asciiParagraph(Paragraph const & par,
1475 unsigned int linelen,
1476 bool noparbreak) const
1478 ostringstream buffer;
1479 Paragraph::depth_type depth = 0;
1481 Paragraph::depth_type ltype_depth = 0;
1482 bool ref_printed = false;
1483 // if (!par->previous()) {
1485 // begins or ends a deeper area ?
1486 if (depth != par->params().depth()) {
1487 if (par->params().depth() > depth) {
1488 while (par->params().depth() > depth) {
1492 while (par->params().depth() < depth) {
1498 depth = par.params().depth();
1501 // First write the layout
1502 string const & tmp = par.layout()->name();
1503 if (compare_no_case(tmp, "itemize") == 0) {
1505 ltype_depth = depth + 1;
1506 } else if (compare_ascii_no_case(tmp, "enumerate") == 0) {
1508 ltype_depth = depth + 1;
1509 } else if (contains(ascii_lowercase(tmp), "ection")) {
1511 ltype_depth = depth + 1;
1512 } else if (contains(ascii_lowercase(tmp), "aragraph")) {
1514 ltype_depth = depth + 1;
1515 } else if (compare_ascii_no_case(tmp, "description") == 0) {
1517 ltype_depth = depth + 1;
1518 } else if (compare_ascii_no_case(tmp, "abstract") == 0) {
1521 } else if (compare_ascii_no_case(tmp, "bibliography") == 0) {
1529 /* maybe some vertical spaces */
1531 /* the labelwidthstring used in lists */
1535 /* some pagebreaks? */
1539 /* what about the alignment */
1541 // lyxerr << "Should this ever happen?" << endl;
1544 // linelen <= 0 is special and means we don't have paragraph breaks
1546 string::size_type currlinelen = 0;
1552 buffer << string(depth * 2, ' ');
1553 currlinelen += depth * 2;
1556 // we should probably change to the paragraph language in the
1557 // gettext here (if possible) so that strings are outputted in
1558 // the correct language! (20012712 Jug)
1562 case 4: // (Sub)Paragraph
1563 case 5: // Description
1567 buffer << _("Abstract") << "\n\n";
1570 string const abst = _("Abstract: ");
1572 currlinelen += abst.length();
1575 case 7: // Bibliography
1578 buffer << _("References") << "\n\n";
1581 string const refs = _("References: ");
1583 currlinelen += refs.length();
1591 string const parlab = par.params().labelString();
1592 buffer << parlab << ' ';
1593 currlinelen += parlab.length() + 1;
1601 pair<int, string> p = addDepth(depth, ltype_depth);
1603 currlinelen += p.first;
1606 // this is to change the linebreak to do it by word a bit more
1607 // intelligent hopefully! (only in the case where we have a
1608 // max linelenght!) (Jug)
1612 for (pos_type i = 0; i < par.size(); ++i) {
1613 char c = par.getUChar(params, i);
1615 case Paragraph::META_INSET:
1617 Inset const * inset = par.getInset(i);
1621 currlinelen += word.length();
1624 if (inset->ascii(this, buffer, linelen)) {
1625 // to be sure it breaks paragraph
1626 currlinelen += linelen;
1632 case Paragraph::META_NEWLINE:
1634 buffer << word << "\n";
1637 pair<int, string> p = addDepth(depth,
1640 currlinelen = p.first;
1644 case Paragraph::META_HFILL:
1645 buffer << word << "\t";
1646 currlinelen += word.length() + 1;
1653 currlinelen + word.length() > linelen - 10) {
1655 pair<int, string> p =
1656 addDepth(depth, ltype_depth);
1658 currlinelen = p.first;
1661 buffer << word << ' ';
1662 currlinelen += word.length() + 1;
1669 lyxerr[Debug::INFO] <<
1670 "writeAsciiFile: NULL char in structure." << endl;
1672 if ((linelen > 0) &&
1673 (currlinelen + word.length()) > linelen)
1677 pair<int, string> p =
1678 addDepth(depth, ltype_depth);
1680 currlinelen = p.first;
1687 return STRCONV(buffer.str());
1691 void Buffer::writeFileAscii(string const & fname, int linelen)
1693 ofstream ofs(fname.c_str());
1695 Alert::err_alert(_("Error: Cannot write file:"), fname);
1698 writeFileAscii(ofs, linelen);
1702 void Buffer::writeFileAscii(ostream & os, int linelen)
1704 ParagraphList::iterator beg = paragraphs.begin();
1705 ParagraphList::iterator end = paragraphs.end();
1706 ParagraphList::iterator it = beg;
1707 for (; it != end; ++it) {
1708 os << asciiParagraph(*it, linelen, it == beg);
1717 void Buffer::makeLaTeXFile(string const & fname,
1718 string const & original_path,
1719 bool nice, bool only_body, bool only_preamble)
1721 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
1723 ofstream ofs(fname.c_str());
1725 Alert::err_alert(_("Error: Cannot open file: "), fname);
1729 makeLaTeXFile(ofs, original_path, nice, only_body, only_preamble);
1733 lyxerr << "File was not closed properly." << endl;
1738 void Buffer::makeLaTeXFile(ostream & os,
1739 string const & original_path,
1740 bool nice, bool only_body, bool only_preamble)
1742 niceFile = nice; // this will be used by Insetincludes.
1744 // validate the buffer.
1745 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
1746 LaTeXFeatures features(params);
1748 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
1751 // The starting paragraph of the coming rows is the
1752 // first paragraph of the document. (Asger)
1753 texrow.start(&*(paragraphs.begin()), 0);
1755 if (!only_body && nice) {
1756 os << "%% " << lyx_docversion << " created this file. "
1757 "For more info, see http://www.lyx.org/.\n"
1758 "%% Do not edit unless you really know what "
1763 lyxerr[Debug::INFO] << "lyx header finished" << endl;
1764 // There are a few differences between nice LaTeX and usual files:
1765 // usual is \batchmode and has a
1766 // special input@path to allow the including of figures
1767 // with either \input or \includegraphics (what figinsets do).
1768 // input@path is set when the actual parameter
1769 // original_path is set. This is done for usual tex-file, but not
1770 // for nice-latex-file. (Matthias 250696)
1773 // code for usual, NOT nice-latex-file
1774 os << "\\batchmode\n"; // changed
1775 // from \nonstopmode
1778 if (!original_path.empty()) {
1779 string inputpath = os::external_path(original_path);
1780 subst(inputpath, "~", "\\string~");
1781 os << "\\makeatletter\n"
1782 << "\\def\\input@path{{"
1783 << inputpath << "/}}\n"
1784 << "\\makeatother\n";
1790 os << "\\documentclass";
1792 LyXTextClass const & tclass = params.getLyXTextClass();
1794 ostringstream options; // the document class options.
1796 if (tokenPos(tclass.opt_fontsize(),
1797 '|', params.fontsize) >= 0) {
1798 // only write if existing in list (and not default)
1799 options << params.fontsize << "pt,";
1803 if (!params.use_geometry &&
1804 (params.paperpackage == BufferParams::PACKAGE_NONE)) {
1805 switch (params.papersize) {
1806 case BufferParams::PAPER_A4PAPER:
1807 options << "a4paper,";
1809 case BufferParams::PAPER_USLETTER:
1810 options << "letterpaper,";
1812 case BufferParams::PAPER_A5PAPER:
1813 options << "a5paper,";
1815 case BufferParams::PAPER_B5PAPER:
1816 options << "b5paper,";
1818 case BufferParams::PAPER_EXECUTIVEPAPER:
1819 options << "executivepaper,";
1821 case BufferParams::PAPER_LEGALPAPER:
1822 options << "legalpaper,";
1828 if (params.sides != tclass.sides()) {
1829 switch (params.sides) {
1830 case LyXTextClass::OneSide:
1831 options << "oneside,";
1833 case LyXTextClass::TwoSides:
1834 options << "twoside,";
1840 if (params.columns != tclass.columns()) {
1841 if (params.columns == 2)
1842 options << "twocolumn,";
1844 options << "onecolumn,";
1847 if (!params.use_geometry
1848 && params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
1849 options << "landscape,";
1851 // language should be a parameter to \documentclass
1853 ostringstream language_options;
1854 if (params.language->babel() == "hebrew"
1855 && default_language->babel() != "hebrew")
1856 // This seems necessary
1857 features.useLanguage(default_language);
1859 if (lyxrc.language_use_babel ||
1860 params.language->lang() != lyxrc.default_language ||
1861 features.hasLanguages()) {
1863 language_options << features.getLanguages();
1864 language_options << params.language->babel();
1865 if (lyxrc.language_global_options)
1866 options << language_options.str() << ',';
1869 // the user-defined options
1870 if (!params.options.empty()) {
1871 options << params.options << ',';
1874 string strOptions(STRCONV(options.str()));
1875 if (!strOptions.empty()) {
1876 strOptions = rtrim(strOptions, ",");
1877 os << '[' << strOptions << ']';
1880 os << '{' << tclass.latexname() << "}\n";
1882 // end of \documentclass defs
1884 // font selection must be done before loading fontenc.sty
1885 // The ae package is not needed when using OT1 font encoding.
1886 if (params.fonts != "default" &&
1887 (params.fonts != "ae" || lyxrc.fontenc != "default")) {
1888 os << "\\usepackage{" << params.fonts << "}\n";
1890 if (params.fonts == "ae") {
1891 os << "\\usepackage{aecompl}\n";
1895 // this one is not per buffer
1896 if (lyxrc.fontenc != "default") {
1897 os << "\\usepackage[" << lyxrc.fontenc
1902 if (params.inputenc == "auto") {
1903 string const doc_encoding =
1904 params.language->encoding()->LatexName();
1906 // Create a list with all the input encodings used
1908 set<string> encodings = features.getEncodingSet(doc_encoding);
1910 os << "\\usepackage[";
1911 std::copy(encodings.begin(), encodings.end(),
1912 std::ostream_iterator<string>(os, ","));
1913 os << doc_encoding << "]{inputenc}\n";
1915 } else if (params.inputenc != "default") {
1916 os << "\\usepackage[" << params.inputenc
1921 // At the very beginning the text parameters.
1922 if (params.paperpackage != BufferParams::PACKAGE_NONE) {
1923 switch (params.paperpackage) {
1924 case BufferParams::PACKAGE_A4:
1925 os << "\\usepackage{a4}\n";
1928 case BufferParams::PACKAGE_A4WIDE:
1929 os << "\\usepackage{a4wide}\n";
1932 case BufferParams::PACKAGE_WIDEMARGINSA4:
1933 os << "\\usepackage[widemargins]{a4}\n";
1938 if (params.use_geometry) {
1939 os << "\\usepackage{geometry}\n";
1941 os << "\\geometry{verbose";
1942 if (params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
1944 switch (params.papersize2) {
1945 case BufferParams::VM_PAPER_CUSTOM:
1946 if (!params.paperwidth.empty())
1947 os << ",paperwidth="
1948 << params.paperwidth;
1949 if (!params.paperheight.empty())
1950 os << ",paperheight="
1951 << params.paperheight;
1953 case BufferParams::VM_PAPER_USLETTER:
1954 os << ",letterpaper";
1956 case BufferParams::VM_PAPER_USLEGAL:
1957 os << ",legalpaper";
1959 case BufferParams::VM_PAPER_USEXECUTIVE:
1960 os << ",executivepaper";
1962 case BufferParams::VM_PAPER_A3:
1965 case BufferParams::VM_PAPER_A4:
1968 case BufferParams::VM_PAPER_A5:
1971 case BufferParams::VM_PAPER_B3:
1974 case BufferParams::VM_PAPER_B4:
1977 case BufferParams::VM_PAPER_B5:
1981 // default papersize ie BufferParams::VM_PAPER_DEFAULT
1982 switch (lyxrc.default_papersize) {
1983 case BufferParams::PAPER_DEFAULT: // keep compiler happy
1984 case BufferParams::PAPER_USLETTER:
1985 os << ",letterpaper";
1987 case BufferParams::PAPER_LEGALPAPER:
1988 os << ",legalpaper";
1990 case BufferParams::PAPER_EXECUTIVEPAPER:
1991 os << ",executivepaper";
1993 case BufferParams::PAPER_A3PAPER:
1996 case BufferParams::PAPER_A4PAPER:
1999 case BufferParams::PAPER_A5PAPER:
2002 case BufferParams::PAPER_B5PAPER:
2007 if (!params.topmargin.empty())
2008 os << ",tmargin=" << params.topmargin;
2009 if (!params.bottommargin.empty())
2010 os << ",bmargin=" << params.bottommargin;
2011 if (!params.leftmargin.empty())
2012 os << ",lmargin=" << params.leftmargin;
2013 if (!params.rightmargin.empty())
2014 os << ",rmargin=" << params.rightmargin;
2015 if (!params.headheight.empty())
2016 os << ",headheight=" << params.headheight;
2017 if (!params.headsep.empty())
2018 os << ",headsep=" << params.headsep;
2019 if (!params.footskip.empty())
2020 os << ",footskip=" << params.footskip;
2025 if (tokenPos(tclass.opt_pagestyle(),
2026 '|', params.pagestyle) >= 0) {
2027 if (params.pagestyle == "fancy") {
2028 os << "\\usepackage{fancyhdr}\n";
2031 os << "\\pagestyle{" << params.pagestyle << "}\n";
2035 if (params.secnumdepth != tclass.secnumdepth()) {
2036 os << "\\setcounter{secnumdepth}{"
2037 << params.secnumdepth
2041 if (params.tocdepth != tclass.tocdepth()) {
2042 os << "\\setcounter{tocdepth}{"
2048 if (params.paragraph_separation) {
2049 switch (params.defskip.kind()) {
2050 case VSpace::SMALLSKIP:
2051 os << "\\setlength\\parskip{\\smallskipamount}\n";
2053 case VSpace::MEDSKIP:
2054 os << "\\setlength\\parskip{\\medskipamount}\n";
2056 case VSpace::BIGSKIP:
2057 os << "\\setlength\\parskip{\\bigskipamount}\n";
2059 case VSpace::LENGTH:
2060 os << "\\setlength\\parskip{"
2061 << params.defskip.length().asLatexString()
2064 default: // should never happen // Then delete it.
2065 os << "\\setlength\\parskip{\\medskipamount}\n";
2070 os << "\\setlength\\parindent{0pt}\n";
2074 // Now insert the LyX specific LaTeX commands...
2076 // The optional packages;
2077 string preamble(features.getPackages());
2079 // this might be useful...
2080 preamble += "\n\\makeatletter\n";
2082 // Some macros LyX will need
2083 string tmppreamble(features.getMacros());
2085 if (!tmppreamble.empty()) {
2086 preamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2087 "LyX specific LaTeX commands.\n"
2088 + tmppreamble + '\n';
2091 // the text class specific preamble
2092 tmppreamble = features.getTClassPreamble();
2093 if (!tmppreamble.empty()) {
2094 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2095 "Textclass specific LaTeX commands.\n"
2096 + tmppreamble + '\n';
2099 /* the user-defined preamble */
2100 if (!params.preamble.empty()) {
2101 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2102 "User specified LaTeX commands.\n"
2103 + params.preamble + '\n';
2106 // Itemize bullet settings need to be last in case the user
2107 // defines their own bullets that use a package included
2108 // in the user-defined preamble -- ARRae
2109 // Actually it has to be done much later than that
2110 // since some packages like frenchb make modifications
2111 // at \begin{document} time -- JMarc
2113 for (int i = 0; i < 4; ++i) {
2114 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
2115 if (bullets_def.empty())
2116 bullets_def="\\AtBeginDocument{\n";
2117 bullets_def += " \\renewcommand{\\labelitemi";
2119 // `i' is one less than the item to modify
2126 bullets_def += "ii";
2132 bullets_def += "}{" +
2133 params.user_defined_bullets[i].getText()
2138 if (!bullets_def.empty())
2139 preamble += bullets_def + "}\n\n";
2142 int(lyx::count(preamble.begin(), preamble.end(), '\n'));
2143 for (int j = 0; j != nlines; ++j) {
2147 // We try to load babel late, in case it interferes
2148 // with other packages.
2150 string tmp = lyxrc.language_package;
2151 if (!lyxrc.language_global_options
2152 && tmp == "\\usepackage{babel}")
2153 tmp = string("\\usepackage[") +
2154 STRCONV(language_options.str()) +
2156 preamble += tmp + "\n";
2157 preamble += features.getBabelOptions();
2160 preamble += "\\makeatother\n";
2162 // dvipost settings come after everything else
2163 if (params.tracking_changes) {
2164 preamble += "\\dvipostlayout\n";
2165 preamble += "\\dvipost{osstart color push Red}\n";
2166 preamble += "\\dvipost{osend color pop}\n";
2167 preamble += "\\dvipost{cbstart color push Blue}\n";
2168 preamble += "\\dvipost{cbend color pop} \n";
2177 os << "\\begin{document}\n";
2180 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
2182 if (!lyxrc.language_auto_begin) {
2183 os << subst(lyxrc.language_command_begin, "$$lang",
2184 params.language->babel())
2189 latexParagraphs(os, &*(paragraphs.begin()), 0, texrow);
2191 // add this just in case after all the paragraphs
2195 if (!lyxrc.language_auto_end) {
2196 os << subst(lyxrc.language_command_end, "$$lang",
2197 params.language->babel())
2203 os << "\\end{document}\n";
2206 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
2208 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
2212 // Just to be sure. (Asger)
2215 lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
2216 lyxerr[Debug::INFO] << "Row count was " << texrow.rows() - 1
2219 // we want this to be true outside previews (for insetexternal)
2225 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
2227 void Buffer::latexParagraphs(ostream & ofs, Paragraph * par,
2228 Paragraph * endpar, TexRow & texrow,
2229 bool moving_arg) const
2231 bool was_title = false;
2232 bool already_title = false;
2235 while (par != endpar) {
2236 Inset * in = par->inInset();
2237 // well we have to check if we are in an inset with unlimited
2238 // length (all in one row) if that is true then we don't allow
2239 // any special options in the paragraph and also we don't allow
2240 // any environment other then "Standard" to be valid!
2241 if ((in == 0) || !in->forceDefaultParagraphs(in)) {
2242 LyXLayout_ptr const & layout = par->layout();
2244 if (layout->intitle) {
2245 if (already_title) {
2246 lyxerr <<"Error in latexParagraphs: You"
2247 " should not mix title layouts"
2248 " with normal ones." << endl;
2251 } else if (was_title && !already_title) {
2252 ofs << "\\maketitle\n";
2254 already_title = true;
2258 if (layout->isEnvironment() ||
2259 !par->params().leftIndent().zero())
2261 par = par->TeXEnvironment(this, params, ofs, texrow);
2263 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2266 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2269 // It might be that we only have a title in this document
2270 if (was_title && !already_title) {
2271 ofs << "\\maketitle\n";
2277 bool Buffer::isLatex() const
2279 return params.getLyXTextClass().outputType() == LATEX;
2283 bool Buffer::isLinuxDoc() const
2285 return params.getLyXTextClass().outputType() == LINUXDOC;
2289 bool Buffer::isLiterate() const
2291 return params.getLyXTextClass().outputType() == LITERATE;
2295 bool Buffer::isDocBook() const
2297 return params.getLyXTextClass().outputType() == DOCBOOK;
2301 bool Buffer::isSGML() const
2303 LyXTextClass const & tclass = params.getLyXTextClass();
2305 return tclass.outputType() == LINUXDOC ||
2306 tclass.outputType() == DOCBOOK;
2310 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
2312 ofstream ofs(fname.c_str());
2315 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2319 niceFile = nice; // this will be used by included files.
2321 LaTeXFeatures features(params);
2327 LyXTextClass const & tclass = params.getLyXTextClass();
2329 string top_element = tclass.latexname();
2332 ofs << "<!doctype linuxdoc system";
2334 string preamble = params.preamble;
2335 const string name = nice ? ChangeExtension(filename_, ".sgml")
2337 preamble += features.getIncludedFiles(name);
2338 preamble += features.getLyXSGMLEntities();
2340 if (!preamble.empty()) {
2341 ofs << " [ " << preamble << " ]";
2345 if (params.options.empty())
2346 sgml::openTag(ofs, 0, false, top_element);
2348 string top = top_element;
2350 top += params.options;
2351 sgml::openTag(ofs, 0, false, top);
2355 ofs << "<!-- " << lyx_docversion
2356 << " created this file. For more info see http://www.lyx.org/"
2359 Paragraph::depth_type depth = 0; // paragraph depth
2360 Paragraph * par = &*(paragraphs.begin());
2362 vector<string> environment_stack(5);
2365 LyXLayout_ptr const & style = par->layout();
2366 // treat <toc> as a special case for compatibility with old code
2367 if (par->isInset(0)) {
2368 Inset * inset = par->getInset(0);
2369 Inset::Code lyx_code = inset->lyxCode();
2370 if (lyx_code == Inset::TOC_CODE) {
2371 string const temp = "toc";
2372 sgml::openTag(ofs, depth, false, temp);
2379 // environment tag closing
2380 for (; depth > par->params().depth(); --depth) {
2381 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
2382 environment_stack[depth].erase();
2385 // write opening SGML tags
2386 switch (style->latextype) {
2387 case LATEX_PARAGRAPH:
2388 if (depth == par->params().depth()
2389 && !environment_stack[depth].empty()) {
2390 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
2391 environment_stack[depth].erase();
2397 sgml::openTag(ofs, depth, false, style->latexname());
2403 _("Error: Wrong depth for LatexType Command.\n"));
2405 if (!environment_stack[depth].empty()) {
2406 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
2410 environment_stack[depth].erase();
2411 sgml::openTag(ofs, depth, false, style->latexname());
2414 case LATEX_ENVIRONMENT:
2415 case LATEX_ITEM_ENVIRONMENT:
2417 string const & latexname = style->latexname();
2419 if (depth == par->params().depth()
2420 && environment_stack[depth] != latexname) {
2421 sgml::closeTag(ofs, depth, false,
2422 environment_stack[depth]);
2423 environment_stack[depth].erase();
2425 if (depth < par->params().depth()) {
2426 depth = par->params().depth();
2427 environment_stack[depth].erase();
2429 if (environment_stack[depth] != latexname) {
2431 sgml::openTag(ofs, depth, false, "p");
2433 sgml::openTag(ofs, depth, false, latexname);
2435 if (environment_stack.size() == depth + 1)
2436 environment_stack.push_back("!-- --");
2437 environment_stack[depth] = latexname;
2440 if (style->latexparam() == "CDATA")
2443 if (style->latextype == LATEX_ENVIRONMENT) break;
2445 if (style->labeltype == LABEL_MANUAL)
2450 sgml::openTag(ofs, depth + 1, false, item_name);
2455 sgml::openTag(ofs, depth, false, style->latexname());
2459 simpleLinuxDocOnePar(ofs, par, depth);
2464 // write closing SGML tags
2465 switch (style->latextype) {
2468 case LATEX_ENVIRONMENT:
2469 case LATEX_ITEM_ENVIRONMENT:
2470 if (style->latexparam() == "CDATA")
2474 sgml::closeTag(ofs, depth, false, style->latexname());
2480 for (int i = depth; i >= 0; --i)
2481 sgml::closeTag(ofs, depth, false, environment_stack[i]);
2485 sgml::closeTag(ofs, 0, false, top_element);
2489 // How to check for successful close
2491 // we want this to be true outside previews (for insetexternal)
2496 // checks, if newcol chars should be put into this line
2497 // writes newline, if necessary.
2500 void sgmlLineBreak(ostream & os, string::size_type & colcount,
2501 string::size_type newcol)
2504 if (colcount > lyxrc.ascii_linelen) {
2506 colcount = newcol; // assume write after this call
2521 string tag_name(PAR_TAG const & pt) {
2523 case NONE: return "!-- --";
2524 case TT: return "tt";
2525 case SF: return "sf";
2526 case BF: return "bf";
2527 case IT: return "it";
2528 case SL: return "sl";
2529 case EM: return "em";
2536 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2538 p1 = static_cast<PAR_TAG>(p1 | p2);
2543 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2545 p1 = static_cast<PAR_TAG>(p1 & ~p2);
2551 // Handle internal paragraph parsing -- layout already processed.
2552 void Buffer::simpleLinuxDocOnePar(ostream & os,
2554 Paragraph::depth_type /*depth*/)
2556 LyXLayout_ptr const & style = par->layout();
2558 string::size_type char_line_count = 5; // Heuristic choice ;-)
2560 // gets paragraph main font
2563 if (style->labeltype == LABEL_MANUAL) {
2564 font_old = style->labelfont;
2567 font_old = style->font;
2571 LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
2572 LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
2573 LyXFont::FONT_SHAPE shape_type = LyXFont::UP_SHAPE;
2576 stack<PAR_TAG> tag_state;
2577 // parsing main loop
2578 for (pos_type i = 0; i < par->size(); ++i) {
2580 PAR_TAG tag_close = NONE;
2581 list < PAR_TAG > tag_open;
2583 LyXFont const font = par->getFont(params, i);
2585 if (font_old.family() != font.family()) {
2586 switch (family_type) {
2587 case LyXFont::SANS_FAMILY:
2590 case LyXFont::TYPEWRITER_FAMILY:
2597 family_type = font.family();
2599 switch (family_type) {
2600 case LyXFont::SANS_FAMILY:
2601 tag_open.push_back(SF);
2603 case LyXFont::TYPEWRITER_FAMILY:
2604 tag_open.push_back(TT);
2611 if (font_old.series() != font.series()) {
2612 switch (series_type) {
2613 case LyXFont::BOLD_SERIES:
2620 series_type = font.series();
2622 switch (series_type) {
2623 case LyXFont::BOLD_SERIES:
2624 tag_open.push_back(BF);
2632 if (font_old.shape() != font.shape()) {
2633 switch (shape_type) {
2634 case LyXFont::ITALIC_SHAPE:
2637 case LyXFont::SLANTED_SHAPE:
2644 shape_type = font.shape();
2646 switch (shape_type) {
2647 case LyXFont::ITALIC_SHAPE:
2648 tag_open.push_back(IT);
2650 case LyXFont::SLANTED_SHAPE:
2651 tag_open.push_back(SL);
2658 if (font_old.emph() != font.emph()) {
2659 if (font.emph() == LyXFont::ON) {
2660 tag_open.push_back(EM);
2669 list < PAR_TAG > temp;
2670 while (!tag_state.empty() && tag_close) {
2671 PAR_TAG k = tag_state.top();
2673 os << "</" << tag_name(k) << '>';
2680 for(list< PAR_TAG >::const_iterator j = temp.begin();
2681 j != temp.end(); ++j) {
2683 os << '<' << tag_name(*j) << '>';
2686 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
2687 j != tag_open.end(); ++j) {
2689 os << '<' << tag_name(*j) << '>';
2692 char c = par->getChar(i);
2694 if (c == Paragraph::META_INSET) {
2695 Inset * inset = par->getInset(i);
2696 inset->linuxdoc(this, os);
2701 if (style->latexparam() == "CDATA") {
2702 // "TeX"-Mode on == > SGML-Mode on.
2709 boost::tie(ws, str) = sgml::escapeChar(c);
2710 if (ws && !style->free_spacing && !par->isFreeSpacing()) {
2711 // in freespacing mode, spaces are
2712 // non-breaking characters
2713 if (desc_on) {// if char is ' ' then...
2716 sgmlLineBreak(os, char_line_count, 6);
2720 sgmlLineBreak(os, char_line_count, 1);
2725 char_line_count += str.length();
2731 while (!tag_state.empty()) {
2732 os << "</" << tag_name(tag_state.top()) << '>';
2736 // resets description flag correctly
2738 // <tag> not closed...
2739 sgmlLineBreak(os, char_line_count, 6);
2745 // Print an error message.
2746 void Buffer::sgmlError(Paragraph * /*par*/, int /*pos*/,
2747 string const & /*message*/) const
2749 #ifdef WITH_WARNINGS
2750 #warning This is wrong we cannot insert an inset like this!!!
2751 // I guess this was Jose' so I explain you more or less why this
2752 // is wrong. This way you insert something in the paragraph and
2753 // don't tell it to LyXText (row rebreaking and undo handling!!!)
2754 // I deactivate this code, have a look at BufferView::insertErrors
2755 // how you should do this correctly! (Jug 20020315)
2758 // insert an error marker in text
2759 InsetError * new_inset = new InsetError(message);
2760 par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
2766 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
2768 ofstream ofs(fname.c_str());
2770 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2774 Paragraph * par = &*(paragraphs.begin());
2776 niceFile = nice; // this will be used by Insetincludes.
2778 LaTeXFeatures features(params);
2783 LyXTextClass const & tclass = params.getLyXTextClass();
2784 string top_element = tclass.latexname();
2787 ofs << "<!DOCTYPE " << top_element
2788 << " PUBLIC \"-//OASIS//DTD DocBook V4.1//EN\"";
2790 string preamble = params.preamble;
2791 const string name = nice ? ChangeExtension(filename_, ".sgml")
2793 preamble += features.getIncludedFiles(name);
2794 preamble += features.getLyXSGMLEntities();
2796 if (!preamble.empty()) {
2797 ofs << "\n [ " << preamble << " ]";
2802 string top = top_element;
2804 top += params.language->code();
2807 if (!params.options.empty()) {
2809 top += params.options;
2811 sgml::openTag(ofs, 0, false, top);
2813 ofs << "<!-- DocBook file was created by " << lyx_docversion
2814 << "\n See http://www.lyx.org/ for more information -->\n";
2816 vector<string> environment_stack(10);
2817 vector<string> environment_inner(10);
2818 vector<string> command_stack(10);
2820 bool command_flag = false;
2821 Paragraph::depth_type command_depth = 0;
2822 Paragraph::depth_type command_base = 0;
2823 Paragraph::depth_type cmd_depth = 0;
2824 Paragraph::depth_type depth = 0; // paragraph depth
2827 string command_name;
2833 int desc_on = 0; // description mode
2835 LyXLayout_ptr const & style = par->layout();
2837 // environment tag closing
2838 for (; depth > par->params().depth(); --depth) {
2839 if (environment_inner[depth] != "!-- --") {
2840 item_name = "listitem";
2841 sgml::closeTag(ofs, command_depth + depth, false, item_name);
2842 if (environment_inner[depth] == "varlistentry")
2843 sgml::closeTag(ofs, depth+command_depth, false, environment_inner[depth]);
2845 sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2846 environment_stack[depth].erase();
2847 environment_inner[depth].erase();
2850 if (depth == par->params().depth()
2851 && environment_stack[depth] != style->latexname()
2852 && !environment_stack[depth].empty()) {
2853 if (environment_inner[depth] != "!-- --") {
2854 item_name= "listitem";
2855 sgml::closeTag(ofs, command_depth+depth, false, item_name);
2856 if (environment_inner[depth] == "varlistentry")
2857 sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
2860 sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2862 environment_stack[depth].erase();
2863 environment_inner[depth].erase();
2866 // Write opening SGML tags.
2867 switch (style->latextype) {
2868 case LATEX_PARAGRAPH:
2869 sgml::openTag(ofs, depth + command_depth,
2870 false, style->latexname());
2876 _("Error: Wrong depth for LatexType Command.\n"));
2878 command_name = style->latexname();
2880 sgmlparam = style->latexparam();
2881 c_params = split(sgmlparam, c_depth,'|');
2883 cmd_depth = lyx::atoi(c_depth);
2886 if (cmd_depth < command_base) {
2887 for (Paragraph::depth_type j = command_depth;
2888 j >= command_base; --j) {
2889 sgml::closeTag(ofs, j, false, command_stack[j]);
2892 command_depth = command_base = cmd_depth;
2893 } else if (cmd_depth <= command_depth) {
2894 for (int j = command_depth;
2895 j >= int(cmd_depth); --j) {
2896 sgml::closeTag(ofs, j, false, command_stack[j]);
2899 command_depth = cmd_depth;
2901 command_depth = cmd_depth;
2903 command_depth = command_base = cmd_depth;
2904 command_flag = true;
2906 if (command_stack.size() == command_depth + 1)
2907 command_stack.push_back(string());
2908 command_stack[command_depth] = command_name;
2910 // treat label as a special case for
2911 // more WYSIWYM handling.
2912 // This is a hack while paragraphs can't have
2913 // attributes, like id in this case.
2914 if (par->isInset(0)) {
2915 Inset * inset = par->getInset(0);
2916 Inset::Code lyx_code = inset->lyxCode();
2917 if (lyx_code == Inset::LABEL_CODE) {
2918 command_name += " id=\"";
2919 command_name += (static_cast<InsetCommand *>(inset))->getContents();
2920 command_name += '"';
2925 sgml::openTag(ofs, depth + command_depth, false, command_name);
2927 item_name = c_params.empty() ? "title" : c_params;
2928 sgml::openTag(ofs, depth + 1 + command_depth, false, item_name);
2931 case LATEX_ENVIRONMENT:
2932 case LATEX_ITEM_ENVIRONMENT:
2933 if (depth < par->params().depth()) {
2934 depth = par->params().depth();
2935 environment_stack[depth].erase();
2938 if (environment_stack[depth] != style->latexname()) {
2939 if (environment_stack.size() == depth + 1) {
2940 environment_stack.push_back("!-- --");
2941 environment_inner.push_back("!-- --");
2943 environment_stack[depth] = style->latexname();
2944 environment_inner[depth] = "!-- --";
2945 sgml::openTag(ofs, depth + command_depth, false, environment_stack[depth]);
2947 if (environment_inner[depth] != "!-- --") {
2948 item_name= "listitem";
2949 sgml::closeTag(ofs, command_depth + depth, false, item_name);
2950 if (environment_inner[depth] == "varlistentry")
2951 sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
2955 if (style->latextype == LATEX_ENVIRONMENT) {
2956 if (!style->latexparam().empty()) {
2957 if (style->latexparam() == "CDATA")
2960 sgml::openTag(ofs, depth + command_depth, false, style->latexparam());
2965 desc_on = (style->labeltype == LABEL_MANUAL);
2967 environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
2968 sgml::openTag(ofs, depth + 1 + command_depth,
2969 false, environment_inner[depth]);
2971 item_name = desc_on ? "term" : "para";
2972 sgml::openTag(ofs, depth + 1 + command_depth,
2976 sgml::openTag(ofs, depth + command_depth,
2977 false, style->latexname());
2981 simpleDocBookOnePar(ofs, par, desc_on,
2982 depth + 1 + command_depth);
2986 // write closing SGML tags
2987 switch (style->latextype) {
2989 end_tag = c_params.empty() ? "title" : c_params;
2990 sgml::closeTag(ofs, depth + command_depth,
2993 case LATEX_ENVIRONMENT:
2994 if (!style->latexparam().empty()) {
2995 if (style->latexparam() == "CDATA")
2998 sgml::closeTag(ofs, depth + command_depth, false, style->latexparam());
3001 case LATEX_ITEM_ENVIRONMENT:
3002 if (desc_on == 1) break;
3004 sgml::closeTag(ofs, depth + 1 + command_depth, false, end_tag);
3006 case LATEX_PARAGRAPH:
3007 sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
3010 sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
3016 for (int d = depth; d >= 0; --d) {
3017 if (!environment_stack[depth].empty()) {
3018 if (environment_inner[depth] != "!-- --") {
3019 item_name = "listitem";
3020 sgml::closeTag(ofs, command_depth + depth, false, item_name);
3021 if (environment_inner[depth] == "varlistentry")
3022 sgml::closeTag(ofs, depth + command_depth, false, environment_inner[depth]);
3025 sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
3029 for (int j = command_depth; j >= 0 ; --j)
3030 if (!command_stack[j].empty()) {
3031 sgml::closeTag(ofs, j, false, command_stack[j]);
3036 sgml::closeTag(ofs, 0, false, top_element);
3039 // How to check for successful close
3041 // we want this to be true outside previews (for insetexternal)
3046 void Buffer::simpleDocBookOnePar(ostream & os,
3047 Paragraph * par, int & desc_on,
3048 Paragraph::depth_type depth) const
3050 bool emph_flag = false;
3052 LyXLayout_ptr const & style = par->layout();
3054 LyXFont font_old = (style->labeltype == LABEL_MANUAL ? style->labelfont : style->font);
3056 int char_line_count = depth;
3057 //if (!style.free_spacing)
3058 // os << string(depth,' ');
3060 // parsing main loop
3061 for (pos_type i = 0; i < par->size(); ++i) {
3062 LyXFont font = par->getFont(params, i);
3064 // handle <emphasis> tag
3065 if (font_old.emph() != font.emph()) {
3066 if (font.emph() == LyXFont::ON) {
3067 if (style->latexparam() == "CDATA")
3070 if (style->latexparam() == "CDATA")
3074 if (style->latexparam() == "CDATA")
3076 os << "</emphasis>";
3077 if (style->latexparam() == "CDATA")
3084 if (par->isInset(i)) {
3085 Inset * inset = par->getInset(i);
3086 // don't print the inset in position 0 if desc_on == 3 (label)
3087 if (i || desc_on != 3) {
3088 if (style->latexparam() == "CDATA")
3090 inset->docbook(this, os, false);
3091 if (style->latexparam() == "CDATA")
3095 char c = par->getChar(i);
3098 boost::tie(ws, str) = sgml::escapeChar(c);
3100 if (style->pass_thru) {
3102 } else if (style->free_spacing || par->isFreeSpacing() || c != ' ') {
3104 } else if (desc_on ==1) {
3106 os << "\n</term><listitem><para>";
3116 if (style->latexparam() == "CDATA")
3118 os << "</emphasis>";
3119 if (style->latexparam() == "CDATA")
3123 // resets description flag correctly
3125 // <term> not closed...
3126 os << "</term>\n<listitem><para> </para>";
3128 if (style->free_spacing)
3133 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
3134 // Other flags: -wall -v0 -x
3135 int Buffer::runChktex()
3137 if (!users->text) return 0;
3139 users->owner()->prohibitInput();
3141 // get LaTeX-Filename
3142 string const name = getLatexName();
3143 string path = filePath();
3145 string const org_path = path;
3146 if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
3150 Path p(path); // path to LaTeX file
3151 users->owner()->message(_("Running chktex..."));
3153 // Remove all error insets
3154 bool const removedErrorInsets = users->removeAutoInsets();
3156 // Generate the LaTeX file if neccessary
3157 makeLaTeXFile(name, org_path, false);
3160 Chktex chktex(lyxrc.chktex_command, name, filePath());
3161 int res = chktex.run(terr); // run chktex
3164 Alert::alert(_("chktex did not work!"),
3165 _("Could not run with file:"), name);
3166 } else if (res > 0) {
3167 // Insert all errors as errors boxes
3168 users->insertErrors(terr);
3171 // if we removed error insets before we ran chktex or if we inserted
3172 // error insets after we ran chktex, this must be run:
3173 if (removedErrorInsets || res) {
3174 #warning repaint needed here, or do you mean update() ?
3178 users->owner()->allowInput();
3184 void Buffer::validate(LaTeXFeatures & features) const
3186 LyXTextClass const & tclass = params.getLyXTextClass();
3188 if (params.tracking_changes) {
3189 features.require("dvipost");
3190 features.require("color");
3193 // AMS Style is at document level
3194 if (params.use_amsmath || tclass.provides(LyXTextClass::amsmath))
3195 features.require("amsmath");
3197 for_each(paragraphs.begin(), paragraphs.end(),
3198 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
3200 // the bullet shapes are buffer level not paragraph level
3201 // so they are tested here
3202 for (int i = 0; i < 4; ++i) {
3203 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
3204 int const font = params.user_defined_bullets[i].getFont();
3206 int const c = params
3207 .user_defined_bullets[i]
3214 features.require("latexsym");
3216 } else if (font == 1) {
3217 features.require("amssymb");
3218 } else if ((font >= 2 && font <= 5)) {
3219 features.require("pifont");
3224 if (lyxerr.debugging(Debug::LATEX)) {
3225 features.showStruct();
3230 vector<string> const Buffer::getLabelList() const
3232 /// if this is a child document and the parent is already loaded
3233 /// Use the parent's list instead [ale990407]
3234 if (!params.parentname.empty()
3235 && bufferlist.exists(params.parentname)) {
3236 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
3238 return tmp->getLabelList();
3241 vector<string> label_list;
3242 for (inset_iterator it = inset_const_iterator_begin();
3243 it != inset_const_iterator_end(); ++it) {
3244 vector<string> const l = it->getLabelList();
3245 label_list.insert(label_list.end(), l.begin(), l.end());
3251 // This is also a buffer property (ale)
3252 vector<pair<string, string> > const Buffer::getBibkeyList() const
3254 typedef pair<string, string> StringPair;
3255 /// if this is a child document and the parent is already loaded
3256 /// Use the parent's list instead [ale990412]
3257 if (!params.parentname.empty() && bufferlist.exists(params.parentname)) {
3258 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
3260 return tmp->getBibkeyList();
3263 vector<StringPair> keys;
3264 ParagraphList::iterator pit = paragraphs.begin();
3265 ParagraphList::iterator pend = paragraphs.end();
3266 for (; pit != pend; ++pit) {
3268 string const key = pit->bibkey->getContents();
3269 string const opt = pit->bibkey->getOptions();
3270 string const ref = pit->asString(this, false);
3271 string const info = opt + "TheBibliographyRef" + ref;
3273 keys.push_back(StringPair(key, info));
3280 // Might be either using bibtex or a child has bibliography
3281 for (inset_iterator it = inset_const_iterator_begin();
3282 it != inset_const_iterator_end(); ++it) {
3283 // Search for Bibtex or Include inset
3284 if (it->lyxCode() == Inset::BIBTEX_CODE) {
3285 vector<StringPair> tmp =
3286 static_cast<InsetBibtex &>(*it).getKeys(this);
3287 keys.insert(keys.end(), tmp.begin(), tmp.end());
3288 } else if (it->lyxCode() == Inset::INCLUDE_CODE) {
3289 vector<StringPair> const tmp =
3290 static_cast<InsetInclude &>(*it).getKeys();
3291 keys.insert(keys.end(), tmp.begin(), tmp.end());
3299 bool Buffer::isDepClean(string const & name) const
3301 DepClean::const_iterator it = dep_clean_.find(name);
3302 if (it == dep_clean_.end())
3308 void Buffer::markDepClean(string const & name)
3310 dep_clean_[name] = true;
3314 bool Buffer::dispatch(string const & command, bool * result)
3316 // Split command string into command and argument
3318 string line = ltrim(command);
3319 string const arg = trim(split(line, cmd, ' '));
3321 return dispatch(lyxaction.LookupFunc(cmd), arg, result);
3325 bool Buffer::dispatch(int action, string const & argument, bool * result)
3327 bool dispatched = true;
3331 bool const tmp = Exporter::Export(this, argument, false);
3344 void Buffer::resizeInsets(BufferView * bv)
3346 /// then remove all LyXText in text-insets
3347 for_each(paragraphs.begin(), paragraphs.end(),
3348 boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
3352 void Buffer::redraw()
3354 #warning repaint needed here, or do you mean update() ?
3360 void Buffer::changeLanguage(Language const * from, Language const * to)
3363 ParIterator end = par_iterator_end();
3364 for (ParIterator it = par_iterator_begin(); it != end; ++it)
3365 (*it)->changeLanguage(params, from, to);
3369 bool Buffer::isMultiLingual()
3371 ParIterator end = par_iterator_end();
3372 for (ParIterator it = par_iterator_begin(); it != end; ++it)
3373 if ((*it)->isMultiLingual(params))
3380 void Buffer::inset_iterator::setParagraph()
3382 while (pit != pend) {
3383 it = pit->insetlist.begin();
3384 if (it != pit->insetlist.end())
3391 Inset * Buffer::getInsetFromID(int id_arg) const
3393 for (inset_iterator it = inset_const_iterator_begin();
3394 it != inset_const_iterator_end(); ++it)
3396 if (it->id() == id_arg)
3398 Inset * in = it->getInsetFromID(id_arg);
3406 Paragraph * Buffer::getParFromID(int id) const
3411 ParagraphList::iterator it = paragraphs.begin();
3412 ParagraphList::iterator end = paragraphs.end();
3413 for (; it != end; ++it) {
3414 if (it->id() == id) {
3417 Paragraph * tmp = it->getParFromID(id);
3426 ParIterator Buffer::par_iterator_begin()
3428 return ParIterator(&*(paragraphs.begin()));
3432 ParIterator Buffer::par_iterator_end()
3434 return ParIterator();
3437 ParConstIterator Buffer::par_iterator_begin() const
3439 return ParConstIterator(&*(paragraphs.begin()));
3443 ParConstIterator Buffer::par_iterator_end() const
3445 return ParConstIterator();
3450 void Buffer::addUser(BufferView * u)
3456 void Buffer::delUser(BufferView *)
3462 Language const * Buffer::getLanguage() const
3464 return params.language;
3468 bool Buffer::isClean() const
3474 bool Buffer::isBakClean() const
3480 void Buffer::markClean() const
3486 // if the .lyx file has been saved, we don't need an
3492 void Buffer::markBakClean()
3498 void Buffer::setUnnamed(bool flag)
3504 bool Buffer::isUnnamed()
3510 void Buffer::markDirty()
3518 DepClean::iterator it = dep_clean_.begin();
3519 DepClean::const_iterator const end = dep_clean_.end();
3521 for (; it != end; ++it) {
3527 string const & Buffer::fileName() const
3533 string const & Buffer::filePath() const
3539 bool Buffer::isReadonly() const
3545 BufferView * Buffer::getUser() const
3551 void Buffer::setParentName(string const & name)
3553 params.parentname = name;
3557 Buffer::inset_iterator::inset_iterator()
3562 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
3569 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
3573 if (it == pit->insetlist.end()) {
3582 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
3584 inset_iterator tmp = *this;
3590 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
3592 return *it.getInset();
3596 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
3598 return it.getInset();
3602 Paragraph * Buffer::inset_iterator::getPar()
3608 lyx::pos_type Buffer::inset_iterator::getPos() const
3614 bool operator==(Buffer::inset_iterator const & iter1,
3615 Buffer::inset_iterator const & iter2)
3617 return iter1.pit == iter2.pit
3618 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
3622 bool operator!=(Buffer::inset_iterator const & iter1,
3623 Buffer::inset_iterator const & iter2)
3625 return !(iter1 == iter2);