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 #pragma implementation
22 #include "bufferlist.h"
23 #include "LyXAction.h"
26 #include "tex-strings.h"
28 #include "bufferview_funcs.h"
34 #include "LaTeXFeatures.h"
41 #include "converter.h"
42 #include "BufferView.h"
43 #include "ParagraphParameters.h"
44 #include "iterators.h"
45 #include "lyxtextclasslist.h"
47 #include "paragraph_funcs.h"
50 #include "frontends/LyXView.h"
52 #include "mathed/formulamacro.h"
53 #include "mathed/formula.h"
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"
82 #include "insets/insettheorem.h"
83 #include "insets/insetlist.h"
85 #include "insets/insetcaption.h"
86 #include "insets/insetfloatlist.h"
88 #include "frontends/Dialogs.h"
89 #include "frontends/Alert.h"
91 #include "graphics/Previews.h"
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"
103 #include <boost/bind.hpp>
104 #include <boost/tuple/tuple.hpp>
105 #include "BoostFormat.h"
117 #include <sys/types.h>
124 #ifndef CXX_GLOBAL_CSTD
136 using std::make_pair;
146 using lyx::textclass_type;
148 // all these externs should eventually be removed.
149 extern BufferList bufferlist;
153 const int LYX_FORMAT = 222;
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)
162 lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
163 filepath_ = OnlyPath(file);
165 if (read_only || lyxrc.use_tempdir) {
166 tmppath = CreateBufferTmpDir();
171 // set initial author
172 authorlist.record(Author(lyxrc.user_name, lyxrc.user_email));
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.
182 // make sure that views using this buffer
187 if (!tmppath.empty()) {
188 DestroyBufferTmpDir(tmppath);
193 // Remove any previewed LaTeX snippets assocoated with this buffer.
194 grfx::Previews::get().removeLoader(this);
198 string const Buffer::getLatexName(bool no_path) const
200 string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
202 return OnlyFilename(name);
208 pair<Buffer::LogType, string> const Buffer::getLogName() const
210 string const filename = getLatexName(false);
212 if (filename.empty())
213 return make_pair(Buffer::latexlog, string());
215 string path = OnlyPath(filename);
217 if (lyxrc.use_tempdir || !IsDirWriteable(path))
220 string const fname = AddName(path,
221 OnlyFilename(ChangeExtension(filename,
224 AddName(path, OnlyFilename(
225 ChangeExtension(filename,
226 formats.extension("literate") + ".out")));
228 // If no Latex log or Build log is newer, show Build log
230 FileInfo const f_fi(fname);
231 FileInfo const b_fi(bname);
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);
238 lyxerr[Debug::FILES] << "Log name calculated as: " << fname << endl;
239 return make_pair(Buffer::latexlog, fname);
243 void Buffer::setReadonly(bool flag)
245 if (read_only != flag) {
248 users->owner()->getDialogs().updateBufferDependent(false);
253 AuthorList & Buffer::authors()
259 /// Update window titles of all users
260 // Should work on a list
261 void Buffer::updateTitles() const
264 users->owner()->updateWindowTitle();
268 /// Reset autosave timer of all users
269 // Should work on a list
270 void Buffer::resetAutosaveTimers() const
273 users->owner()->resetAutosaveTimer();
277 void Buffer::setFileName(string const & newfile)
279 filename_ = MakeAbsPath(newfile);
280 filepath_ = OnlyPath(filename_);
281 setReadonly(IsFileWriteable(filename_) == 0);
286 // We'll remove this later. (Lgb)
289 string last_inset_read;
292 #warning And _why_ is this here? (Lgb)
296 vector<int> author_ids;
301 // candidate for move to BufferView
302 // (at least some parts in the beginning of the func)
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)
316 Paragraph::depth_type depth = 0;
317 bool the_end_read = false;
319 Paragraph * first_par = 0;
320 LyXFont font(LyXFont::ALL_INHERIT, params.language);
323 if (file_format < 216 && params.language->lang() == "hebrew")
324 font.setLanguage(default_language);
329 par->layout(params.getLyXTextClass().defaultLayout());
331 // We are inserting into an existing document
332 users->text->breakParagraph(users);
333 first_par = users->text->ownerParagraph();
336 // We don't want to adopt the parameters from the
337 // document we insert, so we skip until the text begins:
340 string const pretoken = lex.getString();
341 if (pretoken == "\\layout") {
342 lex.pushToken(pretoken);
350 string const token = lex.getString();
352 if (token.empty()) continue;
354 lyxerr[Debug::PARSER] << "Handling token: `"
355 << token << '\'' << endl;
358 parseSingleLyXformat2Token(lex, par, first_par,
366 paragraphs.set(first_par);
368 if (unknown_layouts > 0) {
369 string s = _("Couldn't set the layout for ");
370 if (unknown_layouts == 1) {
371 s += _("one paragraph");
373 s += tostr(unknown_layouts);
374 s += _(" paragraphs");
377 Alert::alert(_("Textclass Loading Error!"), s,
378 boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
380 Alert::alert(_("Textclass Loading Error!"), s,
381 _("When reading ") + fileName());
385 if (unknown_tokens > 0) {
386 string s = _("Encountered ");
387 if (unknown_tokens == 1) {
388 s += _("one unknown token");
390 s += tostr(unknown_tokens);
391 s += _(" unknown tokens");
394 Alert::alert(_("Textclass Loading Error!"), s,
395 boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
397 Alert::alert(_("Textclass Loading Error!"), s,
398 _("When reading ") + fileName());
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;
414 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
415 Paragraph *& first_par,
416 string const & token, int & pos,
417 Paragraph::depth_type & depth,
421 bool the_end_read = false;
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
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);
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);
441 if (file_format < 216 && params.language->lang() == "hebrew")
442 font.setLanguage(default_language);
446 string layoutname = lex.getString();
448 LyXTextClass const & tclass = params.getLyXTextClass();
450 if (layoutname.empty()) {
451 layoutname = tclass.defaultLayoutName();
453 bool hasLayout = tclass.hasLayout(layoutname);
455 lyxerr << "Layout '" << layoutname << "' does not"
456 << " exist in textclass '" << tclass.name()
458 lyxerr << "Trying to use default layout instead."
460 layoutname = tclass.defaultLayoutName();
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)
472 // We should now read until the next "\layout"
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();
482 if (prefixIs(line, "\\layout")) {
486 if (prefixIs(line, "\\begin_inset"))
488 if (prefixIs(line, "\\end_inset")) {
499 // Now we should have the whole layout in ss
500 // we should now be able to give this to the
502 ss << "\\end_inset\n";
504 // This seems like a bug in stringstream.
505 // We really should be able to use ss
507 istringstream is(ss.str());
509 tmplex.setStream(is);
510 Inset * inset = new InsetCaption;
511 inset->Read(this, tmplex);
512 par->InsertInset(pos, inset, font);
519 par = new Paragraph(par);
520 par->layout(params.getLyXTextClass().defaultLayout());
521 if (params.tracking_changes)
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);
535 } else if (token == "\\end_inset") {
536 lyxerr << "Solitary \\end_inset. Missing \\begin_inset?.\n"
537 << "Last inset read was: " << last_inset_read
539 // Simply ignore this. The insets do not have
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") {
547 font.setLyXFamily(lex.getString());
548 } else if (token == "\\series") {
550 font.setLyXSeries(lex.getString());
551 } else if (token == "\\shape") {
553 font.setLyXShape(lex.getString());
554 } else if (token == "\\size") {
556 font.setLyXSize(lex.getString());
557 } else if (token == "\\lang") {
559 string const tok = lex.getString();
560 Language const * lang = languages.getLanguage(tok);
562 font.setLanguage(lang);
564 font.setLanguage(params.language);
565 lex.printError("Unknown language `$$Token'");
567 } else if (token == "\\numeric") {
569 font.setNumber(font.setLyXMisc(lex.getString()));
570 } else if (token == "\\emph") {
572 font.setEmph(font.setLyXMisc(lex.getString()));
573 } else if (token == "\\bar") {
575 string const tok = lex.getString();
576 // This is dirty, but gone with LyX3. (Asger)
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);
584 lex.printError("Unknown bar font flag "
586 } else if (token == "\\noun") {
588 font.setNoun(font.setLyXMisc(lex.getString()));
589 } else if (token == "\\color") {
591 font.setLyXColor(lex.getString());
592 } else if (token == "\\SpecialChar") {
593 LyXLayout_ptr const & layout = par->layout();
595 // Insets don't make sense in a free-spacing context! ---Kayvan
596 if (layout->free_spacing || par->isFreeSpacing()) {
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);
605 lex.printError("Token `$$Token' "
607 "paragraph layout!");
612 Inset * inset = new InsetSpecialChar;
613 inset->read(this, lex);
614 par->insertInset(pos, inset, font, current_change);
617 } else if (token == "\\i") {
618 Inset * inset = new InsetLatexAccent;
619 inset->read(this, lex);
620 par->insertInset(pos, inset, font, current_change);
622 } else if (token == "\\backslash") {
623 par->insertChar(pos, '\\', font, current_change);
625 } else if (token == "\\begin_deeper") {
627 } else if (token == "\\end_deeper") {
629 lex.printError("\\end_deeper: "
630 "depth is already null");
634 } else if (token == "\\begin_preamble") {
635 params.readPreamble(lex);
636 } else if (token == "\\textclass") {
638 pair<bool, textclass_type> pp =
639 textclasslist.NumberOfClass(lex.getString());
641 params.textclass = pp.second;
644 Alert::alert(_("Textclass error"),
645 boost::io::str(boost::format(_("The document uses an unknown textclass \"%1$s\".")) % lex.getString()),
646 _("-- substituting default."));
649 _("Textclass error"),
650 _("The document uses an unknown textclass ")
652 _("-- substituting default."));
654 params.textclass = 0;
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
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."));
668 Alert::alert(_("Textclass Loading Error!"),
669 _("Can't load textclass ")
670 + params.getLyXTextClass().name(),
671 _("-- substituting default."));
673 params.textclass = 0;
675 } else if (token == "\\options") {
677 params.options = lex.getString();
678 } else if (token == "\\language") {
679 params.readLanguage(lex);
680 } else if (token == "\\fontencoding") {
682 } else if (token == "\\inputencoding") {
684 params.inputenc = lex.getString();
685 } else if (token == "\\graphics") {
686 params.readGraphicsDriver(lex);
687 } else if (token == "\\fontscheme") {
689 params.fonts = lex.getString();
690 } else if (token == "\\noindent") {
691 par->params().noindent(true);
692 } else if (token == "\\leftindent") {
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);
714 params.paragraph_separation =
715 static_cast<BufferParams::PARSEP>(tmpret);
716 } else if (token == "\\defskip") {
718 params.defskip = VSpace(lex.getString());
719 } else if (token == "\\quotes_language") {
720 int tmpret = lex.findToken(string_quotes_language);
723 InsetQuotes::quote_language tmpl =
724 InsetQuotes::EnglishQ;
727 tmpl = InsetQuotes::EnglishQ;
730 tmpl = InsetQuotes::SwedishQ;
733 tmpl = InsetQuotes::GermanQ;
736 tmpl = InsetQuotes::PolishQ;
739 tmpl = InsetQuotes::FrenchQ;
742 tmpl = InsetQuotes::DanishQ;
745 params.quotes_language = tmpl;
746 } else if (token == "\\quotes_times") {
748 switch (lex.getInteger()) {
750 params.quotes_times = InsetQuotes::SingleQ;
753 params.quotes_times = InsetQuotes::DoubleQ;
756 } else if (token == "\\papersize") {
757 int tmpret = lex.findToken(string_papersize);
761 params.papersize2 = tmpret;
762 } else if (token == "\\paperpackage") {
763 int tmpret = lex.findToken(string_paperpackages);
766 params.paperpackage = BufferParams::PACKAGE_NONE;
768 params.paperpackage = tmpret;
769 } else if (token == "\\use_geometry") {
771 params.use_geometry = lex.getInteger();
772 } else if (token == "\\use_amsmath") {
774 params.use_amsmath = lex.getInteger();
775 } else if (token == "\\use_natbib") {
777 params.use_natbib = lex.getInteger();
778 } else if (token == "\\use_numerical_citations") {
780 params.use_numerical_citations = lex.getInteger();
781 } else if (token == "\\tracking_changes") {
783 params.tracking_changes = lex.getInteger();
784 // mark the first paragraph
785 if (params.tracking_changes)
787 } else if (token == "\\author") {
789 istringstream ss(lex.getString());
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);
801 static_cast<BufferParams::PAPER_ORIENTATION>(tmpret);
802 } else if (token == "\\paperwidth") {
804 params.paperwidth = lex.getString();
805 } else if (token == "\\paperheight") {
807 params.paperheight = lex.getString();
808 } else if (token == "\\leftmargin") {
810 params.leftmargin = lex.getString();
811 } else if (token == "\\topmargin") {
813 params.topmargin = lex.getString();
814 } else if (token == "\\rightmargin") {
816 params.rightmargin = lex.getString();
817 } else if (token == "\\bottommargin") {
819 params.bottommargin = lex.getString();
820 } else if (token == "\\headheight") {
822 params.headheight = lex.getString();
823 } else if (token == "\\headsep") {
825 params.headsep = lex.getString();
826 } else if (token == "\\footskip") {
828 params.footskip = lex.getString();
829 } else if (token == "\\paperfontsize") {
831 params.fontsize = rtrim(lex.getString());
832 } else if (token == "\\papercolumns") {
834 params.columns = lex.getInteger();
835 } else if (token == "\\papersides") {
837 switch (lex.getInteger()) {
839 case 1: params.sides = LyXTextClass::OneSide; break;
840 case 2: params.sides = LyXTextClass::TwoSides; break;
842 } else if (token == "\\paperpagestyle") {
844 params.pagestyle = rtrim(lex.getString());
845 } else if (token == "\\bullet") {
847 int const index = lex.getInteger();
849 int temp_int = lex.getInteger();
850 params.user_defined_bullets[index].setFont(temp_int);
851 params.temp_bullets[index].setFont(temp_int);
853 temp_int = lex.getInteger();
854 params.user_defined_bullets[index].setCharacter(temp_int);
855 params.temp_bullets[index].setCharacter(temp_int);
857 temp_int = lex.getInteger();
858 params.user_defined_bullets[index].setSize(temp_int);
859 params.temp_bullets[index].setSize(temp_int);
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?
870 } else if (token == "\\bulletLaTeX") {
871 // The bullet class should be able to read this.
873 int const index = lex.getInteger();
875 string temp_str = lex.getString();
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
882 // if the LaTeX entry has spaces. Each element
883 // therefore needs to be read in turn
886 temp_str = lex.getString();
889 params.user_defined_bullets[index].setText(sum_str);
890 params.temp_bullets[index].setText(sum_str);
891 } else if (token == "\\secnumdepth") {
893 params.secnumdepth = lex.getInteger();
894 } else if (token == "\\tocdepth") {
896 params.tocdepth = lex.getInteger();
897 } else if (token == "\\spacing") {
899 string const tmp = rtrim(lex.getString());
900 Spacing::Space tmp_space = Spacing::Default;
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") {
910 tmp_space = Spacing::Other;
911 tmp_val = lex.getFloat();
913 lex.printError("Unknown spacing token: '$$Token'");
915 // Small hack so that files written with klyx will be
918 par->params().spacing(Spacing(tmp_space, tmp_val));
920 params.spacing.set(tmp_space, tmp_val);
922 } else if (token == "\\paragraph_spacing") {
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") {
933 par->params().spacing(Spacing(Spacing::Other,
936 lex.printError("Unknown spacing token: '$$Token'");
938 } else if (token == "\\float_placement") {
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") {
948 VSpace value = VSpace(lex.getString());
949 // only add the length when value > 0 or
951 if ((value.length().len().value() != 0) ||
953 (value.kind() != VSpace::LENGTH))
954 par->params().spaceTop(value);
955 } else if (token == "\\added_space_bottom") {
957 VSpace value = VSpace(lex.getString());
958 // only add the length when value > 0 or
960 if ((value.length().len().value() != 0) ||
962 (value.kind() != VSpace::LENGTH))
963 par->params().spaceBottom(value);
964 } else if (token == "\\labelwidthstring") {
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);
971 } else if (token == "\\LyXTable") {
972 Inset * inset = new InsetTabular(*this);
973 inset->read(this, lex);
974 par->insertInset(pos, inset, font, current_change);
976 } else if (token == "\\hfill") {
977 par->insertChar(pos, Paragraph::META_HFILL, font, current_change);
979 } else if (token == "\\change_unchanged") {
980 // Hack ! Needed for empty paragraphs :/
983 current_change = Change(Change::UNCHANGED);
984 } else if (token == "\\change_inserted") {
986 istringstream istr(lex.getString());
991 current_change = Change(Change::INSERTED, author_ids[aid], ct);
992 } else if (token == "\\change_deleted") {
994 istringstream istr(lex.getString());
999 current_change = Change(Change::DELETED, author_ids[aid], ct);
1000 } else if (token == "\\bibitem") { // ale970302
1002 InsetCommandParams p("bibitem", "dummy");
1003 par->bibkey = new InsetBibKey(p);
1005 par->bibkey->read(this, lex);
1006 } else if (token == "\\the_end") {
1007 the_end_read = true;
1009 // This should be insurance for the future: (Asger)
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();
1017 string const s = _("Unknown token: ") + token
1018 + ' ' + lex.text() + '\n';
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,
1028 return the_end_read;
1032 // needed to insert the selection
1033 void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
1034 LyXFont const & fn,string const & str) const
1036 LyXLayout_ptr const & layout = par->layout();
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) {
1048 if (autobreakrows && (!par->empty() || layout->keepempty)) {
1049 breakParagraph(params, par, pos,
1050 layout->isEnvironment());
1053 space_inserted = true;
1057 // do not insert consecutive spaces if !free_spacing
1058 } else if ((*cit == ' ' || *cit == '\t') &&
1059 space_inserted && !layout->free_spacing &&
1060 !par->isFreeSpacing())
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);
1068 space_inserted = true;
1070 const pos_type nb = 8 - pos % 8;
1071 for (pos_type a = 0; a < nb ; ++a) {
1072 par->insertChar(pos, ' ', font, current_change);
1075 space_inserted = true;
1077 } else if (!IsPrintable(*cit)) {
1078 // Ignore unprintables
1081 // just insert the character
1082 par->insertChar(pos, *cit, font);
1084 space_inserted = (*cit == ' ');
1091 void Buffer::readInset(LyXLex & lex, Paragraph *& par,
1092 int & pos, LyXFont & font)
1094 // consistency check
1095 if (lex.getString() != "\\begin_inset") {
1096 lyxerr << "Buffer::readInset: Consistency check failed."
1103 string const tmptok = lex.getString();
1104 last_inset_read = tmptok;
1106 // test the different insets
1107 if (tmptok == "LatexCommand") {
1108 InsetCommandParams inscmd;
1111 string const cmdName = inscmd.getCmdName();
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);
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);
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"),
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") {
1194 string tmptok = lex.getString();
1195 inset = new InsetFloat(params, tmptok);
1196 } else if (tmptok == "Wrap") {
1198 string tmptok = lex.getString();
1199 inset = new InsetWrap(params, tmptok);
1201 } else if (tmptok == "List") {
1202 inset = new InsetList;
1203 } else if (tmptok == "Theorem") {
1204 inset = new InsetList;
1206 } else if (tmptok == "Caption") {
1207 inset = new InsetCaption(params);
1208 } else if (tmptok == "FloatList") {
1209 inset = new InsetFloatList;
1212 if (inset && !alreadyread) inset->read(this, lex);
1216 par->insertInset(pos, inset, font, current_change);
1222 bool Buffer::readFile(LyXLex & lex, string const & filename, Paragraph * par)
1226 string const token(lex.getString());
1227 if (token == "\\lyxformat") { // the first token _must_ be...
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) {
1240 } else if (file_format > LYX_FORMAT) {
1242 Alert::alert(_("Warning!"),
1243 _("The file was created with a newer version of "
1244 "LyX. This is likely to cause problems."));
1246 } else if (file_format < LYX_FORMAT) {
1248 if (file_format < 200) {
1249 Alert::alert(_("ERROR!"),
1250 _("Old LyX file format found. "
1251 "Use LyX 0.10.x to read this!"));
1253 } else if (!filename.empty()) {
1255 LibFileSearch("lyx2lyx", "lyx2lyx");
1256 if (command.empty()) {
1257 Alert::alert(_("ERROR!"),
1258 _("Can't find conversion script."));
1262 +tostr(LYX_FORMAT) + ' '
1263 + QuoteName(filename);
1264 lyxerr[Debug::INFO] << "Running '"
1267 cmd_ret const ret = RunCommand(command);
1269 Alert::alert(_("ERROR!"),
1270 _("An error occured while "
1271 "running the conversion script."));
1274 istringstream is(STRCONV(ret.second));
1275 LyXLex tmplex(0, 0);
1276 tmplex.setStream(is);
1277 return readFile(tmplex, string(), par);
1279 // This code is reached if lyx2lyx failed (for
1280 // some reason) to change the file format of
1286 bool the_end = readLyXformat2(lex, par);
1287 params.setPaperStuff();
1290 // the_end was added in 213
1291 if (file_format < 213)
1296 Alert::alert(_("Warning!"),
1297 _("Reading of document is not complete"),
1298 _("Maybe the document is truncated"));
1301 } else { // "\\lyxformat" not found
1302 Alert::alert(_("ERROR!"), _("Not a LyX file!"));
1305 Alert::alert(_("ERROR!"), _("Unable to read file!"));
1310 // Should probably be moved to somewhere else: BufferView? LyXView?
1311 bool Buffer::save() const
1313 // We don't need autosaves in the immediate future. (Asger)
1314 resetAutosaveTimers();
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),'/','!'));
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
1338 chmod("LyXVC3.lyx", 0100644) = 0
1339 lseek(0, 0, SEEK_CUR) = 46440
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() };
1354 ifstream ifs(fileName().c_str());
1355 ofstream ofs(s.c_str(), ios::out|ios::trunc);
1360 ::chmod(s.c_str(), fmode);
1362 if (::utime(s.c_str(), ×)) {
1363 lyxerr << "utime error." << endl;
1366 lyxerr << "LyX was not able to make "
1367 "backup copy. Beware." << endl;
1372 if (writeFile(fileName())) {
1374 removeAutosaveFile(fileName());
1376 // Saving failed, so backup is not backup
1377 if (lyxrc.make_backup) {
1378 lyx::rename(s, fileName());
1386 bool Buffer::writeFile(string const & fname) const
1388 if (read_only && (fname == fileName())) {
1392 FileInfo finfo(fname);
1393 if (finfo.exist() && !finfo.writable()) {
1397 ofstream ofs(fname.c_str());
1403 // Use the standard "C" locale for file output.
1404 ofs.imbue(std::locale::classic());
1407 // The top of the file should not be written by params.
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";
1414 // now write out the buffer paramters.
1415 params.writeFile(ofs);
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";
1426 Paragraph::depth_type depth = 0;
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);
1435 // Write marker that shows file is complete
1436 ofs << "\n\\the_end" << endl;
1440 // how to check if close went ok?
1441 // Following is an attempt... (BE 20001011)
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.
1453 lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1455 lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1467 pair<int, string> const addDepth(int depth, int ldepth)
1471 d += (ldepth - depth) * 2;
1472 return make_pair(d, string(d, ' '));
1478 string const Buffer::asciiParagraph(Paragraph const & par,
1479 unsigned int linelen,
1480 bool noparbreak) const
1482 ostringstream buffer;
1483 Paragraph::depth_type depth = 0;
1485 Paragraph::depth_type ltype_depth = 0;
1486 bool ref_printed = false;
1487 // if (!par->previous()) {
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) {
1496 while (par->params().depth() < depth) {
1502 depth = par.params().depth();
1505 // First write the layout
1506 string const & tmp = par.layout()->name();
1507 if (compare_no_case(tmp, "itemize") == 0) {
1509 ltype_depth = depth + 1;
1510 } else if (compare_ascii_no_case(tmp, "enumerate") == 0) {
1512 ltype_depth = depth + 1;
1513 } else if (contains(ascii_lowercase(tmp), "ection")) {
1515 ltype_depth = depth + 1;
1516 } else if (contains(ascii_lowercase(tmp), "aragraph")) {
1518 ltype_depth = depth + 1;
1519 } else if (compare_ascii_no_case(tmp, "description") == 0) {
1521 ltype_depth = depth + 1;
1522 } else if (compare_ascii_no_case(tmp, "abstract") == 0) {
1525 } else if (compare_ascii_no_case(tmp, "bibliography") == 0) {
1533 /* maybe some vertical spaces */
1535 /* the labelwidthstring used in lists */
1539 /* some pagebreaks? */
1543 /* what about the alignment */
1545 // lyxerr << "Should this ever happen?" << endl;
1548 // linelen <= 0 is special and means we don't have paragraph breaks
1550 string::size_type currlinelen = 0;
1556 buffer << string(depth * 2, ' ');
1557 currlinelen += depth * 2;
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)
1566 case 4: // (Sub)Paragraph
1567 case 5: // Description
1571 buffer << _("Abstract") << "\n\n";
1574 string const abst = _("Abstract: ");
1576 currlinelen += abst.length();
1579 case 7: // Bibliography
1582 buffer << _("References") << "\n\n";
1585 string const refs = _("References: ");
1587 currlinelen += refs.length();
1595 string const parlab = par.params().labelString();
1596 buffer << parlab << ' ';
1597 currlinelen += parlab.length() + 1;
1605 pair<int, string> p = addDepth(depth, ltype_depth);
1607 currlinelen += p.first;
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)
1616 for (pos_type i = 0; i < par.size(); ++i) {
1617 char c = par.getUChar(params, i);
1619 case Paragraph::META_INSET:
1621 Inset const * inset = par.getInset(i);
1625 currlinelen += word.length();
1628 if (inset->ascii(this, buffer, linelen)) {
1629 // to be sure it breaks paragraph
1630 currlinelen += linelen;
1636 case Paragraph::META_NEWLINE:
1638 buffer << word << "\n";
1641 pair<int, string> p = addDepth(depth,
1644 currlinelen = p.first;
1648 case Paragraph::META_HFILL:
1649 buffer << word << "\t";
1650 currlinelen += word.length() + 1;
1657 currlinelen + word.length() > linelen - 10) {
1659 pair<int, string> p =
1660 addDepth(depth, ltype_depth);
1662 currlinelen = p.first;
1665 buffer << word << ' ';
1666 currlinelen += word.length() + 1;
1673 lyxerr[Debug::INFO] <<
1674 "writeAsciiFile: NULL char in structure." << endl;
1676 if ((linelen > 0) &&
1677 (currlinelen + word.length()) > linelen)
1681 pair<int, string> p =
1682 addDepth(depth, ltype_depth);
1684 currlinelen = p.first;
1691 return STRCONV(buffer.str());
1695 void Buffer::writeFileAscii(string const & fname, int linelen)
1697 ofstream ofs(fname.c_str());
1699 Alert::err_alert(_("Error: Cannot write file:"), fname);
1702 writeFileAscii(ofs, linelen);
1706 void Buffer::writeFileAscii(ostream & os, int linelen)
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);
1721 void Buffer::makeLaTeXFile(string const & fname,
1722 string const & original_path,
1723 bool nice, bool only_body, bool only_preamble)
1725 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
1727 ofstream ofs(fname.c_str());
1729 Alert::err_alert(_("Error: Cannot open file: "), fname);
1733 makeLaTeXFile(ofs, original_path, nice, only_body, only_preamble);
1737 lyxerr << "File was not closed properly." << endl;
1742 void Buffer::makeLaTeXFile(ostream & os,
1743 string const & original_path,
1744 bool nice, bool only_body, bool only_preamble)
1746 niceFile = nice; // this will be used by Insetincludes.
1748 // validate the buffer.
1749 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
1750 LaTeXFeatures features(params);
1752 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
1755 // The starting paragraph of the coming rows is the
1756 // first paragraph of the document. (Asger)
1757 texrow.start(&*(paragraphs.begin()), 0);
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 "
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)
1777 // code for usual, NOT nice-latex-file
1778 os << "\\batchmode\n"; // changed
1779 // from \nonstopmode
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";
1794 os << "\\documentclass";
1796 LyXTextClass const & tclass = params.getLyXTextClass();
1798 ostringstream options; // the document class options.
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,";
1807 if (!params.use_geometry &&
1808 (params.paperpackage == BufferParams::PACKAGE_NONE)) {
1809 switch (params.papersize) {
1810 case BufferParams::PAPER_A4PAPER:
1811 options << "a4paper,";
1813 case BufferParams::PAPER_USLETTER:
1814 options << "letterpaper,";
1816 case BufferParams::PAPER_A5PAPER:
1817 options << "a5paper,";
1819 case BufferParams::PAPER_B5PAPER:
1820 options << "b5paper,";
1822 case BufferParams::PAPER_EXECUTIVEPAPER:
1823 options << "executivepaper,";
1825 case BufferParams::PAPER_LEGALPAPER:
1826 options << "legalpaper,";
1832 if (params.sides != tclass.sides()) {
1833 switch (params.sides) {
1834 case LyXTextClass::OneSide:
1835 options << "oneside,";
1837 case LyXTextClass::TwoSides:
1838 options << "twoside,";
1844 if (params.columns != tclass.columns()) {
1845 if (params.columns == 2)
1846 options << "twocolumn,";
1848 options << "onecolumn,";
1851 if (!params.use_geometry
1852 && params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
1853 options << "landscape,";
1855 // language should be a parameter to \documentclass
1857 ostringstream language_options;
1858 if (params.language->babel() == "hebrew"
1859 && default_language->babel() != "hebrew")
1860 // This seems necessary
1861 features.useLanguage(default_language);
1863 if (lyxrc.language_use_babel ||
1864 params.language->lang() != lyxrc.default_language ||
1865 features.hasLanguages()) {
1867 language_options << features.getLanguages();
1868 language_options << params.language->babel();
1869 if (lyxrc.language_global_options)
1870 options << language_options.str() << ',';
1873 // the user-defined options
1874 if (!params.options.empty()) {
1875 options << params.options << ',';
1878 string strOptions(STRCONV(options.str()));
1879 if (!strOptions.empty()) {
1880 strOptions = rtrim(strOptions, ",");
1881 os << '[' << strOptions << ']';
1884 os << '{' << tclass.latexname() << "}\n";
1886 // end of \documentclass defs
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";
1894 if (params.fonts == "ae") {
1895 os << "\\usepackage{aecompl}\n";
1899 // this one is not per buffer
1900 if (lyxrc.fontenc != "default") {
1901 os << "\\usepackage[" << lyxrc.fontenc
1906 if (params.inputenc == "auto") {
1907 string const doc_encoding =
1908 params.language->encoding()->LatexName();
1910 // Create a list with all the input encodings used
1912 set<string> encodings = features.getEncodingSet(doc_encoding);
1914 os << "\\usepackage[";
1915 std::copy(encodings.begin(), encodings.end(),
1916 std::ostream_iterator<string>(os, ","));
1917 os << doc_encoding << "]{inputenc}\n";
1919 } else if (params.inputenc != "default") {
1920 os << "\\usepackage[" << params.inputenc
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";
1932 case BufferParams::PACKAGE_A4WIDE:
1933 os << "\\usepackage{a4wide}\n";
1936 case BufferParams::PACKAGE_WIDEMARGINSA4:
1937 os << "\\usepackage[widemargins]{a4}\n";
1942 if (params.use_geometry) {
1943 os << "\\usepackage{geometry}\n";
1945 os << "\\geometry{verbose";
1946 if (params.orientation == BufferParams::ORIENTATION_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;
1957 case BufferParams::VM_PAPER_USLETTER:
1958 os << ",letterpaper";
1960 case BufferParams::VM_PAPER_USLEGAL:
1961 os << ",legalpaper";
1963 case BufferParams::VM_PAPER_USEXECUTIVE:
1964 os << ",executivepaper";
1966 case BufferParams::VM_PAPER_A3:
1969 case BufferParams::VM_PAPER_A4:
1972 case BufferParams::VM_PAPER_A5:
1975 case BufferParams::VM_PAPER_B3:
1978 case BufferParams::VM_PAPER_B4:
1981 case BufferParams::VM_PAPER_B5:
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";
1991 case BufferParams::PAPER_LEGALPAPER:
1992 os << ",legalpaper";
1994 case BufferParams::PAPER_EXECUTIVEPAPER:
1995 os << ",executivepaper";
1997 case BufferParams::PAPER_A3PAPER:
2000 case BufferParams::PAPER_A4PAPER:
2003 case BufferParams::PAPER_A5PAPER:
2006 case BufferParams::PAPER_B5PAPER:
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;
2029 if (tokenPos(tclass.opt_pagestyle(),
2030 '|', params.pagestyle) >= 0) {
2031 if (params.pagestyle == "fancy") {
2032 os << "\\usepackage{fancyhdr}\n";
2035 os << "\\pagestyle{" << params.pagestyle << "}\n";
2039 if (params.secnumdepth != tclass.secnumdepth()) {
2040 os << "\\setcounter{secnumdepth}{"
2041 << params.secnumdepth
2045 if (params.tocdepth != tclass.tocdepth()) {
2046 os << "\\setcounter{tocdepth}{"
2052 if (params.paragraph_separation) {
2053 switch (params.defskip.kind()) {
2054 case VSpace::SMALLSKIP:
2055 os << "\\setlength\\parskip{\\smallskipamount}\n";
2057 case VSpace::MEDSKIP:
2058 os << "\\setlength\\parskip{\\medskipamount}\n";
2060 case VSpace::BIGSKIP:
2061 os << "\\setlength\\parskip{\\bigskipamount}\n";
2063 case VSpace::LENGTH:
2064 os << "\\setlength\\parskip{"
2065 << params.defskip.length().asLatexString()
2068 default: // should never happen // Then delete it.
2069 os << "\\setlength\\parskip{\\medskipamount}\n";
2074 os << "\\setlength\\parindent{0pt}\n";
2078 // Now insert the LyX specific LaTeX commands...
2080 // The optional packages;
2081 string preamble(features.getPackages());
2083 // this might be useful...
2084 preamble += "\n\\makeatletter\n";
2086 // Some macros LyX will need
2087 string tmppreamble(features.getMacros());
2089 if (!tmppreamble.empty()) {
2090 preamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2091 "LyX specific LaTeX commands.\n"
2092 + tmppreamble + '\n';
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';
2103 /* the user-defined preamble */
2104 if (!params.preamble.empty()) {
2105 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2106 "User specified LaTeX commands.\n"
2107 + params.preamble + '\n';
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
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";
2123 // `i' is one less than the item to modify
2130 bullets_def += "ii";
2136 bullets_def += "}{" +
2137 params.user_defined_bullets[i].getText()
2142 if (!bullets_def.empty())
2143 preamble += bullets_def + "}\n\n";
2146 int(lyx::count(preamble.begin(), preamble.end(), '\n'));
2147 for (int j = 0; j != nlines; ++j) {
2151 // We try to load babel late, in case it interferes
2152 // with other packages.
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()) +
2160 preamble += tmp + "\n";
2161 preamble += features.getBabelOptions();
2164 preamble += "\\makeatother\n";
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";
2181 os << "\\begin{document}\n";
2184 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
2186 if (!lyxrc.language_auto_begin) {
2187 os << subst(lyxrc.language_command_begin, "$$lang",
2188 params.language->babel())
2193 latexParagraphs(os, &*(paragraphs.begin()), 0, texrow);
2195 // add this just in case after all the paragraphs
2199 if (!lyxrc.language_auto_end) {
2200 os << subst(lyxrc.language_command_end, "$$lang",
2201 params.language->babel())
2207 os << "\\end{document}\n";
2210 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
2212 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
2216 // Just to be sure. (Asger)
2219 lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
2220 lyxerr[Debug::INFO] << "Row count was " << texrow.rows() - 1
2223 // we want this to be true outside previews (for insetexternal)
2229 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
2231 void Buffer::latexParagraphs(ostream & ofs, Paragraph * par,
2232 Paragraph * endpar, TexRow & texrow,
2233 bool moving_arg) const
2235 bool was_title = false;
2236 bool already_title = false;
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();
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;
2255 } else if (was_title && !already_title) {
2256 ofs << "\\maketitle\n";
2258 already_title = true;
2262 if (layout->isEnvironment() ||
2263 !par->params().leftIndent().zero())
2265 par = par->TeXEnvironment(this, params, ofs, texrow);
2267 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2270 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2273 // It might be that we only have a title in this document
2274 if (was_title && !already_title) {
2275 ofs << "\\maketitle\n";
2281 bool Buffer::isLatex() const
2283 return params.getLyXTextClass().outputType() == LATEX;
2287 bool Buffer::isLinuxDoc() const
2289 return params.getLyXTextClass().outputType() == LINUXDOC;
2293 bool Buffer::isLiterate() const
2295 return params.getLyXTextClass().outputType() == LITERATE;
2299 bool Buffer::isDocBook() const
2301 return params.getLyXTextClass().outputType() == DOCBOOK;
2305 bool Buffer::isSGML() const
2307 LyXTextClass const & tclass = params.getLyXTextClass();
2309 return tclass.outputType() == LINUXDOC ||
2310 tclass.outputType() == DOCBOOK;
2314 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
2316 ofstream ofs(fname.c_str());
2319 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2323 niceFile = nice; // this will be used by included files.
2325 LaTeXFeatures features(params);
2331 LyXTextClass const & tclass = params.getLyXTextClass();
2333 string top_element = tclass.latexname();
2336 ofs << "<!doctype linuxdoc system";
2338 string preamble = params.preamble;
2339 const string name = nice ? ChangeExtension(filename_, ".sgml")
2341 preamble += features.getIncludedFiles(name);
2342 preamble += features.getLyXSGMLEntities();
2344 if (!preamble.empty()) {
2345 ofs << " [ " << preamble << " ]";
2349 if (params.options.empty())
2350 sgml::openTag(ofs, 0, false, top_element);
2352 string top = top_element;
2354 top += params.options;
2355 sgml::openTag(ofs, 0, false, top);
2359 ofs << "<!-- " << lyx_docversion
2360 << " created this file. For more info see http://www.lyx.org/"
2363 Paragraph::depth_type depth = 0; // paragraph depth
2364 Paragraph * par = &*(paragraphs.begin());
2366 vector<string> environment_stack(5);
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);
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();
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();
2401 sgml::openTag(ofs, depth, false, style->latexname());
2407 _("Error: Wrong depth for LatexType Command.\n"));
2409 if (!environment_stack[depth].empty()) {
2410 sgml::closeTag(ofs, depth, false, environment_stack[depth]);
2414 environment_stack[depth].erase();
2415 sgml::openTag(ofs, depth, false, style->latexname());
2418 case LATEX_ENVIRONMENT:
2419 case LATEX_ITEM_ENVIRONMENT:
2421 string const & latexname = style->latexname();
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();
2429 if (depth < par->params().depth()) {
2430 depth = par->params().depth();
2431 environment_stack[depth].erase();
2433 if (environment_stack[depth] != latexname) {
2435 sgml::openTag(ofs, depth, false, "p");
2437 sgml::openTag(ofs, depth, false, latexname);
2439 if (environment_stack.size() == depth + 1)
2440 environment_stack.push_back("!-- --");
2441 environment_stack[depth] = latexname;
2444 if (style->latexparam() == "CDATA")
2447 if (style->latextype == LATEX_ENVIRONMENT) break;
2449 if (style->labeltype == LABEL_MANUAL)
2454 sgml::openTag(ofs, depth + 1, false, item_name);
2459 sgml::openTag(ofs, depth, false, style->latexname());
2463 simpleLinuxDocOnePar(ofs, par, depth);
2468 // write closing SGML tags
2469 switch (style->latextype) {
2472 case LATEX_ENVIRONMENT:
2473 case LATEX_ITEM_ENVIRONMENT:
2474 if (style->latexparam() == "CDATA")
2478 sgml::closeTag(ofs, depth, false, style->latexname());
2484 for (int i = depth; i >= 0; --i)
2485 sgml::closeTag(ofs, depth, false, environment_stack[i]);
2489 sgml::closeTag(ofs, 0, false, top_element);
2493 // How to check for successful close
2495 // we want this to be true outside previews (for insetexternal)
2500 // checks, if newcol chars should be put into this line
2501 // writes newline, if necessary.
2504 void sgmlLineBreak(ostream & os, string::size_type & colcount,
2505 string::size_type newcol)
2508 if (colcount > lyxrc.ascii_linelen) {
2510 colcount = newcol; // assume write after this call
2525 string tag_name(PAR_TAG const & 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";
2540 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2542 p1 = static_cast<PAR_TAG>(p1 | p2);
2547 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2549 p1 = static_cast<PAR_TAG>(p1 & ~p2);
2555 // Handle internal paragraph parsing -- layout already processed.
2556 void Buffer::simpleLinuxDocOnePar(ostream & os,
2558 Paragraph::depth_type /*depth*/)
2560 LyXLayout_ptr const & style = par->layout();
2562 string::size_type char_line_count = 5; // Heuristic choice ;-)
2564 // gets paragraph main font
2567 if (style->labeltype == LABEL_MANUAL) {
2568 font_old = style->labelfont;
2571 font_old = style->font;
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;
2580 stack<PAR_TAG> tag_state;
2581 // parsing main loop
2582 for (pos_type i = 0; i < par->size(); ++i) {
2584 PAR_TAG tag_close = NONE;
2585 list < PAR_TAG > tag_open;
2587 LyXFont const font = par->getFont(params, i);
2589 if (font_old.family() != font.family()) {
2590 switch (family_type) {
2591 case LyXFont::SANS_FAMILY:
2594 case LyXFont::TYPEWRITER_FAMILY:
2601 family_type = font.family();
2603 switch (family_type) {
2604 case LyXFont::SANS_FAMILY:
2605 tag_open.push_back(SF);
2607 case LyXFont::TYPEWRITER_FAMILY:
2608 tag_open.push_back(TT);
2615 if (font_old.series() != font.series()) {
2616 switch (series_type) {
2617 case LyXFont::BOLD_SERIES:
2624 series_type = font.series();
2626 switch (series_type) {
2627 case LyXFont::BOLD_SERIES:
2628 tag_open.push_back(BF);
2636 if (font_old.shape() != font.shape()) {
2637 switch (shape_type) {
2638 case LyXFont::ITALIC_SHAPE:
2641 case LyXFont::SLANTED_SHAPE:
2648 shape_type = font.shape();
2650 switch (shape_type) {
2651 case LyXFont::ITALIC_SHAPE:
2652 tag_open.push_back(IT);
2654 case LyXFont::SLANTED_SHAPE:
2655 tag_open.push_back(SL);
2662 if (font_old.emph() != font.emph()) {
2663 if (font.emph() == LyXFont::ON) {
2664 tag_open.push_back(EM);
2673 list < PAR_TAG > temp;
2674 while (!tag_state.empty() && tag_close) {
2675 PAR_TAG k = tag_state.top();
2677 os << "</" << tag_name(k) << '>';
2684 for(list< PAR_TAG >::const_iterator j = temp.begin();
2685 j != temp.end(); ++j) {
2687 os << '<' << tag_name(*j) << '>';
2690 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
2691 j != tag_open.end(); ++j) {
2693 os << '<' << tag_name(*j) << '>';
2696 char c = par->getChar(i);
2698 if (c == Paragraph::META_INSET) {
2699 Inset * inset = par->getInset(i);
2700 inset->linuxdoc(this, os);
2705 if (style->latexparam() == "CDATA") {
2706 // "TeX"-Mode on == > SGML-Mode on.
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...
2720 sgmlLineBreak(os, char_line_count, 6);
2724 sgmlLineBreak(os, char_line_count, 1);
2729 char_line_count += str.length();
2735 while (!tag_state.empty()) {
2736 os << "</" << tag_name(tag_state.top()) << '>';
2740 // resets description flag correctly
2742 // <tag> not closed...
2743 sgmlLineBreak(os, char_line_count, 6);
2749 // Print an error message.
2750 void Buffer::sgmlError(Paragraph * /*par*/, int /*pos*/,
2751 string const & /*message*/) const
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)
2762 // insert an error marker in text
2763 InsetError * new_inset = new InsetError(message);
2764 par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
2770 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
2772 ofstream ofs(fname.c_str());
2774 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2778 Paragraph * par = &*(paragraphs.begin());
2780 niceFile = nice; // this will be used by Insetincludes.
2782 LaTeXFeatures features(params);
2787 LyXTextClass const & tclass = params.getLyXTextClass();
2788 string top_element = tclass.latexname();
2791 ofs << "<!DOCTYPE " << top_element
2792 << " PUBLIC \"-//OASIS//DTD DocBook V4.1//EN\"";
2794 string preamble = params.preamble;
2795 const string name = nice ? ChangeExtension(filename_, ".sgml")
2797 preamble += features.getIncludedFiles(name);
2798 preamble += features.getLyXSGMLEntities();
2800 if (!preamble.empty()) {
2801 ofs << "\n [ " << preamble << " ]";
2806 string top = top_element;
2808 top += params.language->code();
2811 if (!params.options.empty()) {
2813 top += params.options;
2815 sgml::openTag(ofs, 0, false, top);
2817 ofs << "<!-- DocBook file was created by " << lyx_docversion
2818 << "\n See http://www.lyx.org/ for more information -->\n";
2820 vector<string> environment_stack(10);
2821 vector<string> environment_inner(10);
2822 vector<string> command_stack(10);
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
2831 string command_name;
2837 int desc_on = 0; // description mode
2839 LyXLayout_ptr const & style = par->layout();
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]);
2849 sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2850 environment_stack[depth].erase();
2851 environment_inner[depth].erase();
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]);
2864 sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
2866 environment_stack[depth].erase();
2867 environment_inner[depth].erase();
2870 // Write opening SGML tags.
2871 switch (style->latextype) {
2872 case LATEX_PARAGRAPH:
2873 sgml::openTag(ofs, depth + command_depth,
2874 false, style->latexname());
2880 _("Error: Wrong depth for LatexType Command.\n"));
2882 command_name = style->latexname();
2884 sgmlparam = style->latexparam();
2885 c_params = split(sgmlparam, c_depth,'|');
2887 cmd_depth = lyx::atoi(c_depth);
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]);
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]);
2903 command_depth = cmd_depth;
2905 command_depth = cmd_depth;
2907 command_depth = command_base = cmd_depth;
2908 command_flag = true;
2910 if (command_stack.size() == command_depth + 1)
2911 command_stack.push_back(string());
2912 command_stack[command_depth] = command_name;
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 += '"';
2929 sgml::openTag(ofs, depth + command_depth, false, command_name);
2931 item_name = c_params.empty() ? "title" : c_params;
2932 sgml::openTag(ofs, depth + 1 + command_depth, false, item_name);
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();
2942 if (environment_stack[depth] != style->latexname()) {
2943 if (environment_stack.size() == depth + 1) {
2944 environment_stack.push_back("!-- --");
2945 environment_inner.push_back("!-- --");
2947 environment_stack[depth] = style->latexname();
2948 environment_inner[depth] = "!-- --";
2949 sgml::openTag(ofs, depth + command_depth, false, environment_stack[depth]);
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]);
2959 if (style->latextype == LATEX_ENVIRONMENT) {
2960 if (!style->latexparam().empty()) {
2961 if (style->latexparam() == "CDATA")
2964 sgml::openTag(ofs, depth + command_depth, false, style->latexparam());
2969 desc_on = (style->labeltype == LABEL_MANUAL);
2971 environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
2972 sgml::openTag(ofs, depth + 1 + command_depth,
2973 false, environment_inner[depth]);
2975 item_name = desc_on ? "term" : "para";
2976 sgml::openTag(ofs, depth + 1 + command_depth,
2980 sgml::openTag(ofs, depth + command_depth,
2981 false, style->latexname());
2985 simpleDocBookOnePar(ofs, par, desc_on,
2986 depth + 1 + command_depth);
2990 // write closing SGML tags
2991 switch (style->latextype) {
2993 end_tag = c_params.empty() ? "title" : c_params;
2994 sgml::closeTag(ofs, depth + command_depth,
2997 case LATEX_ENVIRONMENT:
2998 if (!style->latexparam().empty()) {
2999 if (style->latexparam() == "CDATA")
3002 sgml::closeTag(ofs, depth + command_depth, false, style->latexparam());
3005 case LATEX_ITEM_ENVIRONMENT:
3006 if (desc_on == 1) break;
3008 sgml::closeTag(ofs, depth + 1 + command_depth, false, end_tag);
3010 case LATEX_PARAGRAPH:
3011 sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
3014 sgml::closeTag(ofs, depth + command_depth, false, style->latexname());
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]);
3029 sgml::closeTag(ofs, depth + command_depth, false, environment_stack[depth]);
3033 for (int j = command_depth; j >= 0 ; --j)
3034 if (!command_stack[j].empty()) {
3035 sgml::closeTag(ofs, j, false, command_stack[j]);
3040 sgml::closeTag(ofs, 0, false, top_element);
3043 // How to check for successful close
3045 // we want this to be true outside previews (for insetexternal)
3050 void Buffer::simpleDocBookOnePar(ostream & os,
3051 Paragraph * par, int & desc_on,
3052 Paragraph::depth_type depth) const
3054 bool emph_flag = false;
3056 LyXLayout_ptr const & style = par->layout();
3058 LyXFont font_old = (style->labeltype == LABEL_MANUAL ? style->labelfont : style->font);
3060 int char_line_count = depth;
3061 //if (!style.free_spacing)
3062 // os << string(depth,' ');
3064 // parsing main loop
3065 for (pos_type i = 0; i < par->size(); ++i) {
3066 LyXFont font = par->getFont(params, i);
3068 // handle <emphasis> tag
3069 if (font_old.emph() != font.emph()) {
3070 if (font.emph() == LyXFont::ON) {
3071 if (style->latexparam() == "CDATA")
3074 if (style->latexparam() == "CDATA")
3078 if (style->latexparam() == "CDATA")
3080 os << "</emphasis>";
3081 if (style->latexparam() == "CDATA")
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")
3094 inset->docbook(this, os, false);
3095 if (style->latexparam() == "CDATA")
3099 char c = par->getChar(i);
3102 boost::tie(ws, str) = sgml::escapeChar(c);
3104 if (style->pass_thru) {
3106 } else if (style->free_spacing || par->isFreeSpacing() || c != ' ') {
3108 } else if (desc_on ==1) {
3110 os << "\n</term><listitem><para>";
3120 if (style->latexparam() == "CDATA")
3122 os << "</emphasis>";
3123 if (style->latexparam() == "CDATA")
3127 // resets description flag correctly
3129 // <term> not closed...
3130 os << "</term>\n<listitem><para> </para>";
3132 if (style->free_spacing)
3137 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
3138 // Other flags: -wall -v0 -x
3139 int Buffer::runChktex()
3141 if (!users->text) return 0;
3143 users->owner()->prohibitInput();
3145 // get LaTeX-Filename
3146 string const name = getLatexName();
3147 string path = filePath();
3149 string const org_path = path;
3150 if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
3154 Path p(path); // path to LaTeX file
3155 users->owner()->message(_("Running chktex..."));
3157 // Remove all error insets
3158 bool const removedErrorInsets = users->removeAutoInsets();
3160 // Generate the LaTeX file if neccessary
3161 makeLaTeXFile(name, org_path, false);
3164 Chktex chktex(lyxrc.chktex_command, name, filePath());
3165 int res = chktex.run(terr); // run chktex
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);
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() ?
3182 users->owner()->allowInput();
3188 void Buffer::validate(LaTeXFeatures & features) const
3190 LyXTextClass const & tclass = params.getLyXTextClass();
3192 if (params.tracking_changes) {
3193 features.require("dvipost");
3194 features.require("color");
3197 // AMS Style is at document level
3198 if (params.use_amsmath || tclass.provides(LyXTextClass::amsmath))
3199 features.require("amsmath");
3201 for_each(paragraphs.begin(), paragraphs.end(),
3202 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
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();
3210 int const c = params
3211 .user_defined_bullets[i]
3218 features.require("latexsym");
3220 } else if (font == 1) {
3221 features.require("amssymb");
3222 } else if ((font >= 2 && font <= 5)) {
3223 features.require("pifont");
3228 if (lyxerr.debugging(Debug::LATEX)) {
3229 features.showStruct();
3234 vector<string> const Buffer::getLabelList() const
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);
3242 return tmp->getLabelList();
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());
3255 // This is also a buffer property (ale)
3256 vector<pair<string, string> > const Buffer::getBibkeyList() const
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);
3264 return tmp->getBibkeyList();
3267 vector<StringPair> keys;
3268 ParagraphList::iterator pit = paragraphs.begin();
3269 ParagraphList::iterator pend = paragraphs.end();
3270 for (; pit != pend; ++pit) {
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;
3277 keys.push_back(StringPair(key, info));
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());
3303 bool Buffer::isDepClean(string const & name) const
3305 DepClean::const_iterator it = dep_clean_.find(name);
3306 if (it == dep_clean_.end())
3312 void Buffer::markDepClean(string const & name)
3314 dep_clean_[name] = true;
3318 bool Buffer::dispatch(string const & command, bool * result)
3320 // Split command string into command and argument
3322 string line = ltrim(command);
3323 string const arg = trim(split(line, cmd, ' '));
3325 return dispatch(lyxaction.LookupFunc(cmd), arg, result);
3329 bool Buffer::dispatch(int action, string const & argument, bool * result)
3331 bool dispatched = true;
3335 bool const tmp = Exporter::Export(this, argument, false);
3348 void Buffer::resizeInsets(BufferView * bv)
3350 /// then remove all LyXText in text-insets
3351 for_each(paragraphs.begin(), paragraphs.end(),
3352 boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
3356 void Buffer::redraw()
3358 #warning repaint needed here, or do you mean update() ?
3364 void Buffer::changeLanguage(Language const * from, Language const * to)
3367 ParIterator end = par_iterator_end();
3368 for (ParIterator it = par_iterator_begin(); it != end; ++it)
3369 (*it)->changeLanguage(params, from, to);
3373 bool Buffer::isMultiLingual()
3375 ParIterator end = par_iterator_end();
3376 for (ParIterator it = par_iterator_begin(); it != end; ++it)
3377 if ((*it)->isMultiLingual(params))
3384 void Buffer::inset_iterator::setParagraph()
3386 while (pit != pend) {
3387 it = pit->insetlist.begin();
3388 if (it != pit->insetlist.end())
3395 Inset * Buffer::getInsetFromID(int id_arg) const
3397 for (inset_iterator it = inset_const_iterator_begin();
3398 it != inset_const_iterator_end(); ++it)
3400 if (it->id() == id_arg)
3402 Inset * in = it->getInsetFromID(id_arg);
3410 Paragraph * Buffer::getParFromID(int id) const
3415 ParagraphList::iterator it = paragraphs.begin();
3416 ParagraphList::iterator end = paragraphs.end();
3417 for (; it != end; ++it) {
3418 if (it->id() == id) {
3421 Paragraph * tmp = it->getParFromID(id);
3430 ParIterator Buffer::par_iterator_begin()
3432 return ParIterator(&*(paragraphs.begin()));
3436 ParIterator Buffer::par_iterator_end()
3438 return ParIterator();
3441 ParConstIterator Buffer::par_iterator_begin() const
3443 return ParConstIterator(&*(paragraphs.begin()));
3447 ParConstIterator Buffer::par_iterator_end() const
3449 return ParConstIterator();
3454 void Buffer::addUser(BufferView * u)
3460 void Buffer::delUser(BufferView *)
3466 Language const * Buffer::getLanguage() const
3468 return params.language;
3472 bool Buffer::isClean() const
3478 bool Buffer::isBakClean() const
3484 void Buffer::markClean() const
3490 // if the .lyx file has been saved, we don't need an
3496 void Buffer::markBakClean()
3502 void Buffer::setUnnamed(bool flag)
3508 bool Buffer::isUnnamed()
3514 void Buffer::markDirty()
3522 DepClean::iterator it = dep_clean_.begin();
3523 DepClean::const_iterator const end = dep_clean_.end();
3525 for (; it != end; ++it) {
3531 string const & Buffer::fileName() const
3537 string const & Buffer::filePath() const
3543 bool Buffer::isReadonly() const
3549 BufferView * Buffer::getUser() const
3555 void Buffer::setParentName(string const & name)
3557 params.parentname = name;
3561 Buffer::inset_iterator::inset_iterator()
3566 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
3573 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
3577 if (it == pit->insetlist.end()) {
3586 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
3588 inset_iterator tmp = *this;
3594 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
3596 return *it.getInset();
3600 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
3602 return it.getInset();
3606 Paragraph * Buffer::inset_iterator::getPar()
3612 lyx::pos_type Buffer::inset_iterator::getPos() const
3618 bool operator==(Buffer::inset_iterator const & iter1,
3619 Buffer::inset_iterator const & iter2)
3621 return iter1.pit == iter2.pit
3622 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
3626 bool operator!=(Buffer::inset_iterator const & iter1,
3627 Buffer::inset_iterator const & iter2)
3629 return !(iter1 == iter2);