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"
24 #include "LyXAction.h"
27 #include "tex-strings.h"
29 #include "bufferview_funcs.h"
35 #include "LaTeXFeatures.h"
42 #include "converter.h"
43 #include "BufferView.h"
44 #include "ParagraphParameters.h"
45 #include "iterators.h"
46 #include "lyxtextclasslist.h"
48 #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/insetminipage.h"
77 #include "insets/insetfloat.h"
78 #include "insets/insettabular.h"
80 #include "insets/insettheorem.h"
81 #include "insets/insetlist.h"
83 #include "insets/insetcaption.h"
84 #include "insets/insetfloatlist.h"
86 #include "frontends/Dialogs.h"
87 #include "frontends/Alert.h"
89 #include "graphics/Previews.h"
91 #include "support/textutils.h"
92 #include "support/filetools.h"
93 #include "support/path.h"
94 #include "support/os.h"
95 #include "support/lyxlib.h"
96 #include "support/FileInfo.h"
97 #include "support/lyxmanip.h"
98 #include "support/lyxalgo.h" // for lyx::count
100 #include <boost/bind.hpp>
101 #include <boost/tuple/tuple.hpp>
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 = 220;
153 Buffer::Buffer(string const & file, bool ronly)
154 : niceFile(true), lyx_clean(true), bak_clean(true),
155 unnamed(false), dep_clean(0), read_only(ronly),
156 filename_(file), users(0), ctrs(new Counters)
158 lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
160 filepath_ = OnlyPath(file);
164 // read_only = ronly;
168 if (read_only || lyxrc.use_tempdir) {
169 tmppath = CreateBufferTmpDir();
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(void) 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 /// Update window titles of all users
254 // Should work on a list
255 void Buffer::updateTitles() const
258 users->owner()->updateWindowTitle();
262 /// Reset autosave timer of all users
263 // Should work on a list
264 void Buffer::resetAutosaveTimers() const
267 users->owner()->resetAutosaveTimer();
271 void Buffer::setFileName(string const & newfile)
273 filename_ = MakeAbsPath(newfile);
274 filepath_ = OnlyPath(filename_);
275 setReadonly(IsFileWriteable(filename_) == 0);
280 // We'll remove this later. (Lgb)
283 string last_inset_read;
285 #ifndef NO_COMPABILITY
288 ErtComp() : active(false), fromlayout(false), in_tabular(false) {
297 std::stack<ErtComp> ert_stack;
302 #warning And _why_ is this here? (Lgb)
310 // candidate for move to BufferView
311 // (at least some parts in the beginning of the func)
314 // changed to be public and have one parameter
315 // if par = 0 normal behavior
316 // else insert behavior
317 // Returns false if "\the_end" is not read for formats >= 2.13. (Asger)
318 bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
322 #ifndef NO_COMPABILITY
323 ert_comp.contents.erase();
324 ert_comp.active = false;
325 ert_comp.fromlayout = false;
326 ert_comp.in_tabular = false;
329 Paragraph::depth_type depth = 0;
330 bool the_end_read = false;
332 Paragraph * first_par = 0;
333 LyXFont font(LyXFont::ALL_INHERIT, params.language);
336 if (file_format < 216 && params.language->lang() == "hebrew")
337 font.setLanguage(default_language);
342 par->layout(params.getLyXTextClass().defaultLayout());
344 // We are inserting into an existing document
345 users->text->breakParagraph(users);
346 first_par = users->text->ownerParagraph();
349 // We don't want to adopt the parameters from the
350 // document we insert, so we skip until the text begins:
353 string const pretoken = lex.getString();
354 if (pretoken == "\\layout") {
355 lex.pushToken(pretoken);
363 string const token = lex.getString();
365 if (token.empty()) continue;
367 lyxerr[Debug::PARSER] << "Handling token: `"
368 << token << "'" << endl;
371 parseSingleLyXformat2Token(lex, par, first_par,
379 paragraphs.set(first_par);
381 if (unknown_layouts > 0) {
382 string s = _("Couldn't set the layout for ");
383 if (unknown_layouts == 1) {
384 s += _("one paragraph");
386 s += tostr(unknown_layouts);
387 s += _(" paragraphs");
389 Alert::alert(_("Textclass Loading Error!"), s,
390 _("When reading " + fileName()));
393 if (unknown_tokens > 0) {
394 string s = _("Encountered ");
395 if (unknown_tokens == 1) {
396 s += _("one unknown token");
398 s += tostr(unknown_tokens);
399 s += _(" unknown tokens");
401 Alert::alert(_("Textclass Loading Error!"), s,
402 _("When reading " + fileName()));
409 #ifndef NO_COMPABILITY
411 Inset * Buffer::isErtInset(Paragraph * par, int pos) const
414 if ((par->getChar(pos) == Paragraph::META_INSET) &&
415 (inset = par->getInset(pos)) &&
416 (inset->lyxCode() == Inset::ERT_CODE))
423 void Buffer::insertErtContents(Paragraph * par, int & pos, bool set_inactive)
425 if (ert_comp.contents.find_first_not_of(' ') != string::npos) {
426 // we only skip completely empty ERT (only spaces) otherwise
427 // we have to insert it as is.
428 string str(ert_comp.contents);
429 lyxerr[Debug::INSETS] << "ERT contents:\n'"
430 << str << "'" << endl;
431 // check if we have already an ert inset a position earlier
432 // if this is the case then we should insert the contents
433 // inside the other ertinset as it is stupid to have various
436 if ((pos > 0) && (inset=isErtInset(par, pos-1))) {
437 // get the last paragraph from the inset before
438 Paragraph * last = inset->firstParagraph();
441 // create the new paragraph after the last one
442 Paragraph * par = new Paragraph(last);
443 par->layout(params.getLyXTextClass().defaultLayoutName());
444 par->setInsetOwner(last->inInset());
446 LyXFont font(LyXFont::ALL_INHERIT, params.language);
447 string::const_iterator cit = str.begin();
448 string::const_iterator end = str.end();
450 for (; cit != end; ++cit) {
451 par->insertChar(pos++, *cit, font);
455 new InsetERT(params, params.language, str, true);
456 par->insertInset(pos++, inset, ert_comp.font);
459 ert_comp.contents.erase();
460 ert_comp.fromlayout = false;
462 ert_comp.active = false;
469 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
470 Paragraph *& first_par,
471 string const & token, int & pos,
472 Paragraph::depth_type & depth,
476 bool the_end_read = false;
478 // The order of the tags tested may seem unnatural, but this
479 // has been done in order to reduce the number of string
480 // comparisons needed to recognize a given token. This leads
481 // on large documents like UserGuide to a reduction of a
483 if (token[0] != '\\') {
484 #ifndef NO_COMPABILITY
485 if (ert_comp.active) {
486 ert_comp.contents += token;
489 for (string::const_iterator cit = token.begin();
490 cit != token.end(); ++cit) {
491 par->insertChar(pos, (*cit), font);
494 #ifndef NO_COMPABILITY
497 } else if (token == "\\layout") {
498 #ifndef NO_COMPABILITY
499 bool old_fromlayout = ert_comp.fromlayout;
500 bool create_new_par = true;
501 ert_comp.in_tabular = false;
503 if (!par->size() && par->previous() &&
504 (par->previous()->size() == 1) &&
505 isErtInset(par->previous(), par->previous()->size()-1))
507 int p = par->previous()->size();
508 insertErtContents(par->previous(), p);
509 create_new_par = false;
511 insertErtContents(par, pos);
514 // reset the font as we start a new layout and if the font is
515 // not ALL_INHERIT,document_language then it will be set to the
516 // right values after this tag (Jug 20020420)
517 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
520 if (file_format < 216 && params.language->lang() == "hebrew")
521 font.setLanguage(default_language);
525 string layoutname = lex.getString();
527 LyXTextClass const & tclass = params.getLyXTextClass();
529 if (layoutname.empty()) {
530 layoutname = tclass.defaultLayoutName();
532 #ifndef NO_COMPABILITY
533 if (compare_ascii_no_case(layoutname, "latex") == 0) {
534 ert_comp.active = true;
535 ert_comp.fromlayout = true;
536 ert_comp.font = font;
538 create_new_par = false;
541 bool hasLayout = tclass.hasLayout(layoutname);
543 lyxerr << "Layout '" << layoutname << "' does not"
544 << " exist in textclass '" << tclass.name()
546 lyxerr << "Trying to use default layout instead."
548 layoutname = tclass.defaultLayoutName();
552 // The is the compability reading of layout caption.
553 // It can be removed in LyX version 1.3.0. (Lgb)
554 if (compare_ascii_no_case(layoutname, "caption") == 0) {
555 // We expect that the par we are now working on is
556 // really inside a InsetText inside a InsetFloat.
557 // We also know that captions can only be
558 // one paragraph. (Lgb)
560 // We should now read until the next "\layout"
562 // This is probably not good enough, what if the
563 // caption is the last par in the document (Lgb)
564 istream & ist = lex.getStream();
570 if (prefixIs(line, "\\layout")) {
574 if (prefixIs(line, "\\begin_inset"))
576 if (prefixIs(line, "\\end_inset")) {
587 // Now we should have the whole layout in ss
588 // we should now be able to give this to the
590 ss << "\\end_inset\n";
592 // This seems like a bug in stringstream.
593 // We really should be able to use ss
595 istringstream is(ss.str());
597 tmplex.setStream(is);
598 Inset * inset = new InsetCaption;
599 inset->Read(this, tmplex);
600 par->InsertInset(pos, inset, font);
604 #ifndef NO_COMPABILITY
605 if (create_new_par) {
610 par = new Paragraph(par);
611 par->layout(params.getLyXTextClass().defaultLayout());
614 par->layout(params.getLyXTextClass()[layoutname]);
615 // Test whether the layout is obsolete.
616 LyXLayout_ptr const & layout = par->layout();
617 if (!layout->obsoleted_by().empty())
618 par->layout(params.getLyXTextClass()[layout->obsoleted_by()]);
619 par->params().depth(depth);
620 #ifndef NO_COMPABILITY
622 // we duplicate code here because it's easier to remove
623 // the code then of NO_COMPATIBILITY
624 par->layout(layoutname);
625 // Test whether the layout is obsolete.
626 LyXLayout_ptr const & layout =
627 params.getLyXTextClass()[par->layout()];
628 if (!layout->obsoleted_by().empty())
629 par->layout(layout->obsoleted_by());
630 par->params().depth(depth);
637 } else if (token == "\\end_inset") {
638 lyxerr << "Solitary \\end_inset. Missing \\begin_inset?.\n"
639 << "Last inset read was: " << last_inset_read
641 // Simply ignore this. The insets do not have
643 // But insets should read it, it is a part of
644 // the inset isn't it? Lgb.
645 } else if (token == "\\begin_inset") {
646 #ifndef NO_COMPABILITY
647 insertErtContents(par, pos, false);
648 ert_stack.push(ert_comp);
649 ert_comp = ErtComp();
651 readInset(lex, par, pos, font);
652 #ifndef NO_COMPABILITY
653 ert_comp = ert_stack.top();
655 insertErtContents(par, pos);
657 } else if (token == "\\family") {
659 font.setLyXFamily(lex.getString());
660 } else if (token == "\\series") {
662 font.setLyXSeries(lex.getString());
663 } else if (token == "\\shape") {
665 font.setLyXShape(lex.getString());
666 } else if (token == "\\size") {
668 font.setLyXSize(lex.getString());
669 #ifndef NO_COMPABILITY
670 } else if (token == "\\latex") {
672 string const tok = lex.getString();
673 if (tok == "no_latex") {
675 insertErtContents(par, pos);
676 } else if (tok == "latex") {
677 ert_comp.active = true;
678 ert_comp.font = font;
679 } else if (tok == "default") {
681 insertErtContents(par, pos);
683 lex.printError("Unknown LaTeX font flag "
687 } else if (token == "\\lang") {
689 string const tok = lex.getString();
690 Language const * lang = languages.getLanguage(tok);
692 font.setLanguage(lang);
694 font.setLanguage(params.language);
695 lex.printError("Unknown language `$$Token'");
697 } else if (token == "\\numeric") {
699 font.setNumber(font.setLyXMisc(lex.getString()));
700 } else if (token == "\\emph") {
702 font.setEmph(font.setLyXMisc(lex.getString()));
703 } else if (token == "\\bar") {
705 string const tok = lex.getString();
706 // This is dirty, but gone with LyX3. (Asger)
708 font.setUnderbar(LyXFont::ON);
709 else if (tok == "no")
710 font.setUnderbar(LyXFont::OFF);
711 else if (tok == "default")
712 font.setUnderbar(LyXFont::INHERIT);
714 lex.printError("Unknown bar font flag "
716 } else if (token == "\\noun") {
718 font.setNoun(font.setLyXMisc(lex.getString()));
719 } else if (token == "\\color") {
721 font.setLyXColor(lex.getString());
722 } else if (token == "\\SpecialChar") {
723 LyXLayout_ptr const & layout = par->layout();
725 // Insets don't make sense in a free-spacing context! ---Kayvan
726 if (layout->free_spacing || par->isFreeSpacing()) {
729 string next_token = lex.getString();
730 if (next_token == "\\-") {
731 par->insertChar(pos, '-', font);
732 } else if (next_token == "\\protected_separator"
733 || next_token == "~") {
734 par->insertChar(pos, ' ', font);
736 lex.printError("Token `$$Token' "
738 "paragraph layout!");
743 Inset * inset = new InsetSpecialChar;
744 inset->read(this, lex);
745 par->insertInset(pos, inset, font);
748 } else if (token == "\\i") {
749 Inset * inset = new InsetLatexAccent;
750 inset->read(this, lex);
751 par->insertInset(pos, inset, font);
753 } else if (token == "\\backslash") {
754 #ifndef NO_COMPABILITY
755 if (ert_comp.active) {
756 ert_comp.contents += "\\";
759 par->insertChar(pos, '\\', font);
761 #ifndef NO_COMPABILITY
764 #ifndef NO_COMPABILITY
765 } else if (token == "\\begin_float") {
766 insertErtContents(par, pos);
767 //insertErtContents(par, pos, false);
768 //ert_stack.push(ert_comp);
769 //ert_comp = ErtComp();
771 // This is the compability reader. It can be removed in
772 // LyX version 1.3.0. (Lgb)
774 string const tmptok = lex.getString();
775 //lyxerr << "old float: " << tmptok << endl;
778 stringstream old_float;
780 if (tmptok == "footnote") {
781 inset = new InsetFoot(params);
782 old_float << "collapsed true\n";
783 } else if (tmptok == "margin") {
784 inset = new InsetMarginal(params);
785 old_float << "collapsed true\n";
786 } else if (tmptok == "fig") {
787 inset = new InsetFloat(params, "figure");
788 old_float << "wide false\n"
789 << "collapsed false\n";
790 } else if (tmptok == "tab") {
791 inset = new InsetFloat(params, "table");
792 old_float << "wide false\n"
793 << "collapsed false\n";
794 } else if (tmptok == "alg") {
795 inset = new InsetFloat(params, "algorithm");
796 old_float << "wide false\n"
797 << "collapsed false\n";
798 } else if (tmptok == "wide-fig") {
799 inset = new InsetFloat(params, "figure");
800 //InsetFloat * tmp = new InsetFloat("figure");
803 old_float << "wide true\n"
804 << "collapsed false\n";
805 } else if (tmptok == "wide-tab") {
806 inset = new InsetFloat(params, "table");
807 //InsetFloat * tmp = new InsetFloat("table");
810 old_float << "wide true\n"
811 << "collapsed false\n";
815 return false; // no end read yet
818 // Here we need to check for \end_deeper and handle that
819 // before we do the footnote parsing.
820 // This _is_ a hack! (Lgb)
823 string const tmp = lex.getString();
824 if (tmp == "\\end_deeper") {
825 //lyxerr << "\\end_deeper caught!" << endl;
827 lex.printError("\\end_deeper: "
828 "depth is already null");
833 old_float << tmp << ' ';
838 old_float << lex.getLongString("\\end_float")
839 << "\n\\end_inset\n";
840 //lyxerr << "Float Body:\n" << old_float.str() << endl;
841 // That this does not work seems like a bug
842 // in stringstream. (Lgb)
843 istringstream istr(old_float.str());
845 nylex.setStream(istr);
846 inset->read(this, nylex);
847 par->insertInset(pos, inset, font);
849 insertErtContents(par, pos);
851 // we have to reset the font as in the old format after a float
852 // the font was automatically reset!
853 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
855 } else if (token == "\\begin_deeper") {
857 } else if (token == "\\end_deeper") {
859 lex.printError("\\end_deeper: "
860 "depth is already null");
864 } else if (token == "\\begin_preamble") {
865 params.readPreamble(lex);
866 } else if (token == "\\textclass") {
868 pair<bool, textclass_type> pp =
869 textclasslist.NumberOfClass(lex.getString());
871 params.textclass = pp.second;
873 Alert::alert(string(_("Textclass error")),
874 string(_("The document uses an unknown textclass \"")) +
875 lex.getString() + string("\"."),
876 string(_("LyX will not be able to produce output correctly.")));
877 params.textclass = 0;
879 if (!params.getLyXTextClass().load()) {
880 // if the textclass wasn't loaded properly
881 // we need to either substitute another
882 // or stop loading the file.
883 // I can substitute but I don't see how I can
884 // stop loading... ideas?? ARRae980418
885 Alert::alert(_("Textclass Loading Error!"),
886 string(_("Can't load textclass ")) +
887 params.getLyXTextClass().name(),
888 _("-- substituting default"));
889 params.textclass = 0;
891 } else if (token == "\\options") {
893 params.options = lex.getString();
894 } else if (token == "\\language") {
895 params.readLanguage(lex);
896 } else if (token == "\\fontencoding") {
898 } else if (token == "\\inputencoding") {
900 params.inputenc = lex.getString();
901 } else if (token == "\\graphics") {
902 params.readGraphicsDriver(lex);
903 } else if (token == "\\fontscheme") {
905 params.fonts = lex.getString();
906 } else if (token == "\\noindent") {
907 par->params().noindent(true);
908 } else if (token == "\\leftindent") {
910 LyXLength value(lex.getString());
911 par->params().leftIndent(value);
912 } else if (token == "\\fill_top") {
913 par->params().spaceTop(VSpace(VSpace::VFILL));
914 } else if (token == "\\fill_bottom") {
915 par->params().spaceBottom(VSpace(VSpace::VFILL));
916 } else if (token == "\\line_top") {
917 par->params().lineTop(true);
918 } else if (token == "\\line_bottom") {
919 par->params().lineBottom(true);
920 } else if (token == "\\pagebreak_top") {
921 par->params().pagebreakTop(true);
922 } else if (token == "\\pagebreak_bottom") {
923 par->params().pagebreakBottom(true);
924 } else if (token == "\\start_of_appendix") {
925 par->params().startOfAppendix(true);
926 } else if (token == "\\paragraph_separation") {
927 int tmpret = lex.findToken(string_paragraph_separation);
930 params.paragraph_separation =
931 static_cast<BufferParams::PARSEP>(tmpret);
932 } else if (token == "\\defskip") {
934 params.defskip = VSpace(lex.getString());
935 #ifndef NO_COMPABILITY
936 } else if (token == "\\epsfig") { // obsolete
937 // Indeed it is obsolete, but we HAVE to be backwards
938 // compatible until 0.14, because otherwise all figures
939 // in existing documents are irretrivably lost. (Asger)
940 params.readGraphicsDriver(lex);
942 } else if (token == "\\quotes_language") {
943 int tmpret = lex.findToken(string_quotes_language);
946 InsetQuotes::quote_language tmpl =
947 InsetQuotes::EnglishQ;
950 tmpl = InsetQuotes::EnglishQ;
953 tmpl = InsetQuotes::SwedishQ;
956 tmpl = InsetQuotes::GermanQ;
959 tmpl = InsetQuotes::PolishQ;
962 tmpl = InsetQuotes::FrenchQ;
965 tmpl = InsetQuotes::DanishQ;
968 params.quotes_language = tmpl;
969 } else if (token == "\\quotes_times") {
971 switch (lex.getInteger()) {
973 params.quotes_times = InsetQuotes::SingleQ;
976 params.quotes_times = InsetQuotes::DoubleQ;
979 } else if (token == "\\papersize") {
980 int tmpret = lex.findToken(string_papersize);
984 params.papersize2 = tmpret;
985 } else if (token == "\\paperpackage") {
986 int tmpret = lex.findToken(string_paperpackages);
989 params.paperpackage = BufferParams::PACKAGE_NONE;
991 params.paperpackage = tmpret;
992 } else if (token == "\\use_geometry") {
994 params.use_geometry = lex.getInteger();
995 } else if (token == "\\use_amsmath") {
997 params.use_amsmath = lex.getInteger();
998 } else if (token == "\\use_natbib") {
1000 params.use_natbib = lex.getInteger();
1001 } else if (token == "\\use_numerical_citations") {
1003 params.use_numerical_citations = lex.getInteger();
1004 } else if (token == "\\paperorientation") {
1005 int tmpret = lex.findToken(string_orientation);
1008 params.orientation =
1009 static_cast<BufferParams::PAPER_ORIENTATION>(tmpret);
1010 } else if (token == "\\paperwidth") {
1012 params.paperwidth = lex.getString();
1013 } else if (token == "\\paperheight") {
1015 params.paperheight = lex.getString();
1016 } else if (token == "\\leftmargin") {
1018 params.leftmargin = lex.getString();
1019 } else if (token == "\\topmargin") {
1021 params.topmargin = lex.getString();
1022 } else if (token == "\\rightmargin") {
1024 params.rightmargin = lex.getString();
1025 } else if (token == "\\bottommargin") {
1027 params.bottommargin = lex.getString();
1028 } else if (token == "\\headheight") {
1030 params.headheight = lex.getString();
1031 } else if (token == "\\headsep") {
1033 params.headsep = lex.getString();
1034 } else if (token == "\\footskip") {
1036 params.footskip = lex.getString();
1037 } else if (token == "\\paperfontsize") {
1039 params.fontsize = rtrim(lex.getString());
1040 } else if (token == "\\papercolumns") {
1042 params.columns = lex.getInteger();
1043 } else if (token == "\\papersides") {
1045 switch (lex.getInteger()) {
1047 case 1: params.sides = LyXTextClass::OneSide; break;
1048 case 2: params.sides = LyXTextClass::TwoSides; break;
1050 } else if (token == "\\paperpagestyle") {
1052 params.pagestyle = rtrim(lex.getString());
1053 } else if (token == "\\bullet") {
1055 int const index = lex.getInteger();
1057 int temp_int = lex.getInteger();
1058 params.user_defined_bullets[index].setFont(temp_int);
1059 params.temp_bullets[index].setFont(temp_int);
1061 temp_int = lex.getInteger();
1062 params.user_defined_bullets[index].setCharacter(temp_int);
1063 params.temp_bullets[index].setCharacter(temp_int);
1065 temp_int = lex.getInteger();
1066 params.user_defined_bullets[index].setSize(temp_int);
1067 params.temp_bullets[index].setSize(temp_int);
1069 string const temp_str = lex.getString();
1070 if (temp_str != "\\end_bullet") {
1071 // this element isn't really necessary for
1072 // parsing but is easier for humans
1073 // to understand bullets. Put it back and
1074 // set a debug message?
1075 lex.printError("\\end_bullet expected, got" + temp_str);
1076 //how can I put it back?
1078 } else if (token == "\\bulletLaTeX") {
1079 // The bullet class should be able to read this.
1081 int const index = lex.getInteger();
1083 string temp_str = lex.getString();
1085 while (temp_str != "\\end_bullet") {
1086 // this loop structure is needed when user
1087 // enters an empty string since the first
1088 // thing returned will be the \\end_bullet
1090 // if the LaTeX entry has spaces. Each element
1091 // therefore needs to be read in turn
1092 sum_str += temp_str;
1094 temp_str = lex.getString();
1097 params.user_defined_bullets[index].setText(sum_str);
1098 params.temp_bullets[index].setText(sum_str);
1099 } else if (token == "\\secnumdepth") {
1101 params.secnumdepth = lex.getInteger();
1102 } else if (token == "\\tocdepth") {
1104 params.tocdepth = lex.getInteger();
1105 } else if (token == "\\spacing") {
1107 string const tmp = rtrim(lex.getString());
1108 Spacing::Space tmp_space = Spacing::Default;
1109 float tmp_val = 0.0;
1110 if (tmp == "single") {
1111 tmp_space = Spacing::Single;
1112 } else if (tmp == "onehalf") {
1113 tmp_space = Spacing::Onehalf;
1114 } else if (tmp == "double") {
1115 tmp_space = Spacing::Double;
1116 } else if (tmp == "other") {
1118 tmp_space = Spacing::Other;
1119 tmp_val = lex.getFloat();
1121 lex.printError("Unknown spacing token: '$$Token'");
1123 // Small hack so that files written with klyx will be
1124 // parsed correctly.
1126 par->params().spacing(Spacing(tmp_space, tmp_val));
1128 params.spacing.set(tmp_space, tmp_val);
1130 } else if (token == "\\paragraph_spacing") {
1132 string const tmp = rtrim(lex.getString());
1133 if (tmp == "single") {
1134 par->params().spacing(Spacing(Spacing::Single));
1135 } else if (tmp == "onehalf") {
1136 par->params().spacing(Spacing(Spacing::Onehalf));
1137 } else if (tmp == "double") {
1138 par->params().spacing(Spacing(Spacing::Double));
1139 } else if (tmp == "other") {
1141 par->params().spacing(Spacing(Spacing::Other,
1144 lex.printError("Unknown spacing token: '$$Token'");
1146 } else if (token == "\\float_placement") {
1148 params.float_placement = lex.getString();
1149 } else if (token == "\\align") {
1150 int tmpret = lex.findToken(string_align);
1151 if (tmpret == -1) ++tmpret;
1152 int const tmpret2 = int(pow(2.0, tmpret));
1153 par->params().align(LyXAlignment(tmpret2));
1154 } else if (token == "\\added_space_top") {
1156 VSpace value = VSpace(lex.getString());
1157 // only add the length when value > 0 or
1159 if ((value.length().len().value() != 0) ||
1161 (value.kind() != VSpace::LENGTH))
1162 par->params().spaceTop(value);
1163 } else if (token == "\\added_space_bottom") {
1165 VSpace value = VSpace(lex.getString());
1166 // only add the length when value > 0 or
1168 if ((value.length().len().value() != 0) ||
1170 (value.kind() != VSpace::LENGTH))
1171 par->params().spaceBottom(value);
1172 } else if (token == "\\labelwidthstring") {
1174 par->params().labelWidthString(lex.getString());
1175 // do not delete this token, it is still needed!
1176 } else if (token == "\\newline") {
1177 #ifndef NO_COMPABILITY
1178 if (!ert_comp.in_tabular && ert_comp.active) {
1179 ert_comp.contents += char(Paragraph::META_NEWLINE);
1181 // Since we cannot know it this is only a regular
1182 // newline or a tabular cell delimter we have to
1183 // handle the ERT here.
1184 insertErtContents(par, pos, false);
1186 par->insertChar(pos, Paragraph::META_NEWLINE, font);
1190 par->insertChar(pos, Paragraph::META_NEWLINE, font);
1193 } else if (token == "\\LyXTable") {
1194 #ifndef NO_COMPABILITY
1195 ert_comp.in_tabular = true;
1197 Inset * inset = new InsetTabular(*this);
1198 inset->read(this, lex);
1199 par->insertInset(pos, inset, font);
1201 } else if (token == "\\hfill") {
1202 par->insertChar(pos, Paragraph::META_HFILL, font);
1204 } else if (token == "\\protected_separator") { // obsolete
1205 // This is a backward compability thingie. (Lgb)
1206 // Remove it later some time...introduced with fileformat
1208 LyXLayout_ptr const & layout = par->layout();
1210 if (layout->free_spacing || par->isFreeSpacing()) {
1211 par->insertChar(pos, ' ', font);
1213 Inset * inset = new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
1214 par->insertInset(pos, inset, font);
1217 } else if (token == "\\bibitem") { // ale970302
1219 InsetCommandParams p("bibitem", "dummy");
1220 par->bibkey = new InsetBibKey(p);
1222 par->bibkey->read(this, lex);
1223 } else if (token == "\\the_end") {
1224 #ifndef NO_COMPABILITY
1225 // If we still have some ert active here we have to insert
1226 // it so we don't loose it. (Lgb)
1227 insertErtContents(par, pos);
1229 the_end_read = true;
1231 #ifndef NO_COMPABILITY
1232 if (ert_comp.active) {
1233 ert_comp.contents += token;
1236 // This should be insurance for the future: (Asger)
1239 string const s = _("Unknown token: ") + token
1240 + " " + lex.text() + "\n";
1241 // we can do this here this way because we're actually reading
1242 // the buffer and don't care about LyXText right now.
1243 InsetError * new_inset = new InsetError(s);
1244 par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
1247 #ifndef NO_COMPABILITY
1252 return the_end_read;
1255 // needed to insert the selection
1256 void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
1257 LyXFont const & fn,string const & str) const
1259 LyXLayout_ptr const & layout = par->layout();
1263 par->checkInsertChar(font);
1264 // insert the string, don't insert doublespace
1265 bool space_inserted = true;
1266 bool autobreakrows = !par->inInset() ||
1267 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
1268 for(string::const_iterator cit = str.begin();
1269 cit != str.end(); ++cit) {
1271 if (autobreakrows && (!par->empty() || layout->keepempty)) {
1272 breakParagraph(params, par, pos,
1273 layout->isEnvironment());
1276 space_inserted = true;
1280 // do not insert consecutive spaces if !free_spacing
1281 } else if ((*cit == ' ' || *cit == '\t') &&
1282 space_inserted && !layout->free_spacing &&
1283 !par->isFreeSpacing())
1286 } else if (*cit == '\t') {
1287 if (!layout->free_spacing && !par->isFreeSpacing()) {
1288 // tabs are like spaces here
1289 par->insertChar(pos, ' ', font);
1291 space_inserted = true;
1293 const pos_type nb = 8 - pos % 8;
1294 for (pos_type a = 0; a < nb ; ++a) {
1295 par->insertChar(pos, ' ', font);
1298 space_inserted = true;
1300 } else if (!IsPrintable(*cit)) {
1301 // Ignore unprintables
1304 // just insert the character
1305 par->insertChar(pos, *cit, font);
1307 space_inserted = (*cit == ' ');
1314 void Buffer::readInset(LyXLex & lex, Paragraph *& par,
1315 int & pos, LyXFont & font)
1317 // consistency check
1318 if (lex.getString() != "\\begin_inset") {
1319 lyxerr << "Buffer::readInset: Consistency check failed."
1326 string const tmptok = lex.getString();
1327 last_inset_read = tmptok;
1329 // test the different insets
1330 if (tmptok == "LatexCommand") {
1331 InsetCommandParams inscmd;
1334 string const cmdName = inscmd.getCmdName();
1336 // This strange command allows LyX to recognize "natbib" style
1337 // citations: citet, citep, Citet etc.
1338 if (compare_ascii_no_case(cmdName.substr(0,4), "cite") == 0) {
1339 inset = new InsetCitation(inscmd);
1340 } else if (cmdName == "bibitem") {
1341 lex.printError("Wrong place for bibitem");
1342 inset = new InsetBibKey(inscmd);
1343 } else if (cmdName == "BibTeX") {
1344 inset = new InsetBibtex(inscmd);
1345 } else if (cmdName == "index") {
1346 inset = new InsetIndex(inscmd);
1347 } else if (cmdName == "include") {
1348 inset = new InsetInclude(inscmd, *this);
1349 } else if (cmdName == "label") {
1350 inset = new InsetLabel(inscmd);
1351 } else if (cmdName == "url"
1352 || cmdName == "htmlurl") {
1353 inset = new InsetUrl(inscmd);
1354 } else if (cmdName == "ref"
1355 || cmdName == "pageref"
1356 || cmdName == "vref"
1357 || cmdName == "vpageref"
1358 || cmdName == "prettyref") {
1359 if (!inscmd.getOptions().empty()
1360 || !inscmd.getContents().empty()) {
1361 inset = new InsetRef(inscmd, *this);
1363 } else if (cmdName == "tableofcontents") {
1364 inset = new InsetTOC(inscmd);
1365 } else if (cmdName == "listofalgorithms") {
1366 inset = new InsetFloatList("algorithm");
1367 } else if (cmdName == "listoffigures") {
1368 inset = new InsetFloatList("figure");
1369 } else if (cmdName == "listoftables") {
1370 inset = new InsetFloatList("table");
1371 } else if (cmdName == "printindex") {
1372 inset = new InsetPrintIndex(inscmd);
1373 } else if (cmdName == "lyxparent") {
1374 inset = new InsetParent(inscmd, *this);
1377 bool alreadyread = false;
1378 if (tmptok == "Quotes") {
1379 inset = new InsetQuotes;
1380 } else if (tmptok == "External") {
1381 inset = new InsetExternal;
1382 } else if (tmptok == "FormulaMacro") {
1383 inset = new InsetFormulaMacro;
1384 } else if (tmptok == "Formula") {
1385 inset = new InsetFormula;
1386 } else if (tmptok == "Figure") { // Backward compatibility
1387 // inset = new InsetFig(100, 100, *this);
1388 inset = new InsetGraphics;
1389 } else if (tmptok == "Graphics") {
1390 inset = new InsetGraphics;
1391 } else if (tmptok == "Info") {// backwards compatibility
1392 inset = new InsetNote(this,
1393 lex.getLongString("\\end_inset"),
1396 } else if (tmptok == "Note") {
1397 inset = new InsetNote(params);
1398 } else if (tmptok == "Include") {
1399 InsetCommandParams p("Include");
1400 inset = new InsetInclude(p, *this);
1401 } else if (tmptok == "ERT") {
1402 inset = new InsetERT(params);
1403 } else if (tmptok == "Tabular") {
1404 inset = new InsetTabular(*this);
1405 } else if (tmptok == "Text") {
1406 inset = new InsetText(params);
1407 } else if (tmptok == "Foot") {
1408 inset = new InsetFoot(params);
1409 } else if (tmptok == "Marginal") {
1410 inset = new InsetMarginal(params);
1411 } else if (tmptok == "Minipage") {
1412 inset = new InsetMinipage(params);
1413 } else if (tmptok == "Float") {
1415 string tmptok = lex.getString();
1416 inset = new InsetFloat(params, tmptok);
1418 } else if (tmptok == "List") {
1419 inset = new InsetList;
1420 } else if (tmptok == "Theorem") {
1421 inset = new InsetList;
1423 } else if (tmptok == "Caption") {
1424 inset = new InsetCaption(params);
1425 } else if (tmptok == "FloatList") {
1426 inset = new InsetFloatList;
1429 if (inset && !alreadyread) inset->read(this, lex);
1433 par->insertInset(pos, inset, font);
1439 bool Buffer::readFile(LyXLex & lex, Paragraph * par)
1443 string const token(lex.getString());
1444 if (token == "\\lyxformat") { // the first token _must_ be...
1446 string tmp_format = lex.getString();
1447 //lyxerr << "LyX Format: `" << tmp_format << "'" << endl;
1448 // if present remove ".," from string.
1449 string::size_type dot = tmp_format.find_first_of(".,");
1450 //lyxerr << " dot found at " << dot << endl;
1451 if (dot != string::npos)
1452 tmp_format.erase(dot, 1);
1453 file_format = strToInt(tmp_format);
1454 if (file_format == LYX_FORMAT) {
1456 } else if (file_format > LYX_FORMAT) {
1458 Alert::alert(_("Warning!"),
1459 _("LyX file format is newer that what"),
1460 _("is supported in this LyX version. Expect some problems."));
1462 } else if (file_format < LYX_FORMAT) {
1464 if (file_format < 200) {
1465 Alert::alert(_("ERROR!"),
1466 _("Old LyX file format found. "
1467 "Use LyX 0.10.x to read this!"));
1469 } else if (file_format < 220) {
1470 string const command = "lyx2lyx "
1471 + QuoteName(filename_);
1472 cmd_ret const ret = RunCommand(command);
1474 Alert::alert(_("ERROR!"),
1475 _("An error occured while "
1476 "running the conversion script."));
1479 istringstream is(ret.second);
1480 LyXLex tmplex(0, 0);
1481 tmplex.setStream(is);
1482 return readFile(tmplex);
1485 bool the_end = readLyXformat2(lex, par);
1486 params.setPaperStuff();
1489 // the_end was added in 213
1490 if (file_format < 213)
1495 Alert::alert(_("Warning!"),
1496 _("Reading of document is not complete"),
1497 _("Maybe the document is truncated"));
1500 } else { // "\\lyxformat" not found
1501 Alert::alert(_("ERROR!"), _("Not a LyX file!"));
1504 Alert::alert(_("ERROR!"), _("Unable to read file!"));
1509 // Should probably be moved to somewhere else: BufferView? LyXView?
1510 bool Buffer::save() const
1512 // We don't need autosaves in the immediate future. (Asger)
1513 resetAutosaveTimers();
1517 if (lyxrc.make_backup) {
1518 s = fileName() + '~';
1519 if (!lyxrc.backupdir_path.empty())
1520 s = AddName(lyxrc.backupdir_path,
1521 subst(os::slashify_path(s),'/','!'));
1523 // Rename is the wrong way of making a backup,
1524 // this is the correct way.
1525 /* truss cp fil fil2:
1526 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
1527 stat("LyXVC.lyx", 0xEFFFF688) = 0
1528 open("LyXVC.lyx", O_RDONLY) = 3
1529 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
1530 fstat(4, 0xEFFFF508) = 0
1531 fstat(3, 0xEFFFF508) = 0
1532 read(3, " # T h i s f i l e w".., 8192) = 5579
1533 write(4, " # T h i s f i l e w".., 5579) = 5579
1534 read(3, 0xEFFFD4A0, 8192) = 0
1537 chmod("LyXVC3.lyx", 0100644) = 0
1538 lseek(0, 0, SEEK_CUR) = 46440
1542 // Should probably have some more error checking here.
1543 // Doing it this way, also makes the inodes stay the same.
1544 // This is still not a very good solution, in particular we
1545 // might loose the owner of the backup.
1546 FileInfo finfo(fileName());
1547 if (finfo.exist()) {
1548 mode_t fmode = finfo.getMode();
1549 struct utimbuf times = {
1550 finfo.getAccessTime(),
1551 finfo.getModificationTime() };
1553 ifstream ifs(fileName().c_str());
1554 ofstream ofs(s.c_str(), ios::out|ios::trunc);
1559 ::chmod(s.c_str(), fmode);
1561 if (::utime(s.c_str(), ×)) {
1562 lyxerr << "utime error." << endl;
1565 lyxerr << "LyX was not able to make "
1566 "backup copy. Beware." << endl;
1571 if (writeFile(fileName())) {
1573 removeAutosaveFile(fileName());
1575 // Saving failed, so backup is not backup
1576 if (lyxrc.make_backup) {
1577 lyx::rename(s, fileName());
1585 bool Buffer::writeFile(string const & fname) const
1587 if (read_only && (fname == fileName())) {
1591 FileInfo finfo(fname);
1592 if (finfo.exist() && !finfo.writable()) {
1596 ofstream ofs(fname.c_str());
1602 // Use the standard "C" locale for file output.
1603 ofs.imbue(std::locale::classic());
1606 // The top of the file should not be written by params.
1608 // write out a comment in the top of the file
1609 ofs << '#' << lyx_docversion
1610 << " created this file. For more info see http://www.lyx.org/\n"
1611 << "\\lyxformat " << LYX_FORMAT << "\n";
1613 // now write out the buffer paramters.
1614 params.writeFile(ofs);
1616 Paragraph::depth_type depth = 0;
1618 // this will write out all the paragraphs
1619 // using recursive descent.
1620 ParagraphList::iterator pit = paragraphs.begin();
1621 ParagraphList::iterator pend = paragraphs.end();
1622 for (; pit != pend; ++pit)
1623 pit->write(this, ofs, params, depth);
1625 // Write marker that shows file is complete
1626 ofs << "\n\\the_end" << endl;
1630 // how to check if close went ok?
1631 // Following is an attempt... (BE 20001011)
1633 // good() returns false if any error occured, including some
1634 // formatting error.
1635 // bad() returns true if something bad happened in the buffer,
1636 // which should include file system full errors.
1643 lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1645 lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1657 pair<int, string> const addDepth(int depth, int ldepth)
1661 d += (ldepth - depth) * 2;
1662 return make_pair(d, string(d, ' '));
1668 string const Buffer::asciiParagraph(Paragraph const & par,
1669 unsigned int linelen,
1670 bool noparbreak) const
1672 ostringstream buffer;
1673 Paragraph::depth_type depth = 0;
1675 Paragraph::depth_type ltype_depth = 0;
1676 bool ref_printed = false;
1677 // if (!par->previous()) {
1679 // begins or ends a deeper area ?
1680 if (depth != par->params().depth()) {
1681 if (par->params().depth() > depth) {
1682 while (par->params().depth() > depth) {
1686 while (par->params().depth() < depth) {
1692 depth = par.params().depth();
1695 // First write the layout
1696 string const & tmp = par.layout()->name();
1697 if (compare_no_case(tmp, "itemize") == 0) {
1699 ltype_depth = depth + 1;
1700 } else if (compare_ascii_no_case(tmp, "enumerate") == 0) {
1702 ltype_depth = depth + 1;
1703 } else if (contains(ascii_lowercase(tmp), "ection")) {
1705 ltype_depth = depth + 1;
1706 } else if (contains(ascii_lowercase(tmp), "aragraph")) {
1708 ltype_depth = depth + 1;
1709 } else if (compare_ascii_no_case(tmp, "description") == 0) {
1711 ltype_depth = depth + 1;
1712 } else if (compare_ascii_no_case(tmp, "abstract") == 0) {
1715 } else if (compare_ascii_no_case(tmp, "bibliography") == 0) {
1723 /* maybe some vertical spaces */
1725 /* the labelwidthstring used in lists */
1729 /* some pagebreaks? */
1733 /* what about the alignment */
1735 // lyxerr << "Should this ever happen?" << endl;
1738 // linelen <= 0 is special and means we don't have paragraph breaks
1740 string::size_type currlinelen = 0;
1746 buffer << string(depth * 2, ' ');
1747 currlinelen += depth * 2;
1750 // we should probably change to the paragraph language in the
1751 // gettext here (if possible) so that strings are outputted in
1752 // the correct language! (20012712 Jug)
1756 case 4: // (Sub)Paragraph
1757 case 5: // Description
1761 buffer << _("Abstract") << "\n\n";
1764 string const abst = _("Abstract: ");
1766 currlinelen += abst.length();
1769 case 7: // Bibliography
1772 buffer << _("References") << "\n\n";
1775 string const refs = _("References: ");
1777 currlinelen += refs.length();
1785 string const parlab = par.params().labelString();
1786 buffer << parlab << " ";
1787 currlinelen += parlab.length() + 1;
1795 pair<int, string> p = addDepth(depth, ltype_depth);
1797 currlinelen += p.first;
1800 // this is to change the linebreak to do it by word a bit more
1801 // intelligent hopefully! (only in the case where we have a
1802 // max linelenght!) (Jug)
1806 for (pos_type i = 0; i < par.size(); ++i) {
1807 char c = par.getUChar(params, i);
1809 case Paragraph::META_INSET:
1811 Inset const * inset = par.getInset(i);
1815 currlinelen += word.length();
1818 if (inset->ascii(this, buffer, linelen)) {
1819 // to be sure it breaks paragraph
1820 currlinelen += linelen;
1826 case Paragraph::META_NEWLINE:
1828 buffer << word << "\n";
1831 pair<int, string> p = addDepth(depth,
1834 currlinelen = p.first;
1838 case Paragraph::META_HFILL:
1839 buffer << word << "\t";
1840 currlinelen += word.length() + 1;
1847 currlinelen + word.length() > linelen - 10) {
1849 pair<int, string> p =
1850 addDepth(depth, ltype_depth);
1852 currlinelen = p.first;
1855 buffer << word << ' ';
1856 currlinelen += word.length() + 1;
1863 lyxerr[Debug::INFO] <<
1864 "writeAsciiFile: NULL char in structure." << endl;
1866 if ((linelen > 0) &&
1867 (currlinelen + word.length()) > linelen)
1871 pair<int, string> p =
1872 addDepth(depth, ltype_depth);
1874 currlinelen = p.first;
1881 return buffer.str().c_str();
1885 void Buffer::writeFileAscii(string const & fname, int linelen)
1887 ofstream ofs(fname.c_str());
1889 Alert::err_alert(_("Error: Cannot write file:"), fname);
1892 writeFileAscii(ofs, linelen);
1896 void Buffer::writeFileAscii(ostream & os, int linelen)
1898 ParagraphList::iterator beg = paragraphs.begin();
1899 ParagraphList::iterator end = paragraphs.end();
1900 ParagraphList::iterator it = beg;
1901 for (; it != end; ++it) {
1902 os << asciiParagraph(*it, linelen, it == beg);
1911 void Buffer::makeLaTeXFile(string const & fname,
1912 string const & original_path,
1913 bool nice, bool only_body, bool only_preamble)
1915 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
1917 ofstream ofs(fname.c_str());
1919 Alert::err_alert(_("Error: Cannot open file: "), fname);
1923 makeLaTeXFile(ofs, original_path, nice, only_body, only_preamble);
1927 lyxerr << "File was not closed properly." << endl;
1932 void Buffer::makeLaTeXFile(ostream & os,
1933 string const & original_path,
1934 bool nice, bool only_body, bool only_preamble)
1936 niceFile = nice; // this will be used by Insetincludes.
1938 // validate the buffer.
1939 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
1940 LaTeXFeatures features(params);
1942 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
1945 // The starting paragraph of the coming rows is the
1946 // first paragraph of the document. (Asger)
1947 texrow.start(&*(paragraphs.begin()), 0);
1949 if (!only_body && nice) {
1950 os << "%% " << lyx_docversion << " created this file. "
1951 "For more info, see http://www.lyx.org/.\n"
1952 "%% Do not edit unless you really know what "
1957 lyxerr[Debug::INFO] << "lyx header finished" << endl;
1958 // There are a few differences between nice LaTeX and usual files:
1959 // usual is \batchmode and has a
1960 // special input@path to allow the including of figures
1961 // with either \input or \includegraphics (what figinsets do).
1962 // input@path is set when the actual parameter
1963 // original_path is set. This is done for usual tex-file, but not
1964 // for nice-latex-file. (Matthias 250696)
1967 // code for usual, NOT nice-latex-file
1968 os << "\\batchmode\n"; // changed
1969 // from \nonstopmode
1972 if (!original_path.empty()) {
1973 string inputpath = os::external_path(original_path);
1974 subst(inputpath, "~", "\\string~");
1975 os << "\\makeatletter\n"
1976 << "\\def\\input@path{{"
1977 << inputpath << "/}}\n"
1978 << "\\makeatother\n";
1984 os << "\\documentclass";
1986 LyXTextClass const & tclass = params.getLyXTextClass();
1988 ostringstream options; // the document class options.
1990 if (tokenPos(tclass.opt_fontsize(),
1991 '|', params.fontsize) >= 0) {
1992 // only write if existing in list (and not default)
1993 options << params.fontsize << "pt,";
1997 if (!params.use_geometry &&
1998 (params.paperpackage == BufferParams::PACKAGE_NONE)) {
1999 switch (params.papersize) {
2000 case BufferParams::PAPER_A4PAPER:
2001 options << "a4paper,";
2003 case BufferParams::PAPER_USLETTER:
2004 options << "letterpaper,";
2006 case BufferParams::PAPER_A5PAPER:
2007 options << "a5paper,";
2009 case BufferParams::PAPER_B5PAPER:
2010 options << "b5paper,";
2012 case BufferParams::PAPER_EXECUTIVEPAPER:
2013 options << "executivepaper,";
2015 case BufferParams::PAPER_LEGALPAPER:
2016 options << "legalpaper,";
2022 if (params.sides != tclass.sides()) {
2023 switch (params.sides) {
2024 case LyXTextClass::OneSide:
2025 options << "oneside,";
2027 case LyXTextClass::TwoSides:
2028 options << "twoside,";
2034 if (params.columns != tclass.columns()) {
2035 if (params.columns == 2)
2036 options << "twocolumn,";
2038 options << "onecolumn,";
2041 if (!params.use_geometry
2042 && params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
2043 options << "landscape,";
2045 // language should be a parameter to \documentclass
2047 ostringstream language_options;
2048 if (params.language->babel() == "hebrew"
2049 && default_language->babel() != "hebrew")
2050 // This seems necessary
2051 features.useLanguage(default_language);
2053 if (lyxrc.language_use_babel ||
2054 params.language->lang() != lyxrc.default_language ||
2055 features.hasLanguages()) {
2057 language_options << features.getLanguages();
2058 language_options << params.language->babel();
2059 if (lyxrc.language_global_options)
2060 options << language_options.str() << ',';
2063 // the user-defined options
2064 if (!params.options.empty()) {
2065 options << params.options << ',';
2068 string strOptions(options.str().c_str());
2069 if (!strOptions.empty()) {
2070 strOptions = rtrim(strOptions, ",");
2071 os << '[' << strOptions << ']';
2074 os << '{' << tclass.latexname() << "}\n";
2076 // end of \documentclass defs
2078 // font selection must be done before loading fontenc.sty
2079 // The ae package is not needed when using OT1 font encoding.
2080 if (params.fonts != "default" &&
2081 (params.fonts != "ae" || lyxrc.fontenc != "default")) {
2082 os << "\\usepackage{" << params.fonts << "}\n";
2084 if (params.fonts == "ae") {
2085 os << "\\usepackage{aecompl}\n";
2089 // this one is not per buffer
2090 if (lyxrc.fontenc != "default") {
2091 os << "\\usepackage[" << lyxrc.fontenc
2096 if (params.inputenc == "auto") {
2097 string const doc_encoding =
2098 params.language->encoding()->LatexName();
2100 // Create a list with all the input encodings used
2102 set<string> encodings = features.getEncodingSet(doc_encoding);
2104 os << "\\usepackage[";
2105 std::copy(encodings.begin(), encodings.end(),
2106 std::ostream_iterator<string>(os, ","));
2107 os << doc_encoding << "]{inputenc}\n";
2109 } else if (params.inputenc != "default") {
2110 os << "\\usepackage[" << params.inputenc
2115 // At the very beginning the text parameters.
2116 if (params.paperpackage != BufferParams::PACKAGE_NONE) {
2117 switch (params.paperpackage) {
2118 case BufferParams::PACKAGE_A4:
2119 os << "\\usepackage{a4}\n";
2122 case BufferParams::PACKAGE_A4WIDE:
2123 os << "\\usepackage{a4wide}\n";
2126 case BufferParams::PACKAGE_WIDEMARGINSA4:
2127 os << "\\usepackage[widemargins]{a4}\n";
2132 if (params.use_geometry) {
2133 os << "\\usepackage{geometry}\n";
2135 os << "\\geometry{verbose";
2136 if (params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
2138 switch (params.papersize2) {
2139 case BufferParams::VM_PAPER_CUSTOM:
2140 if (!params.paperwidth.empty())
2141 os << ",paperwidth="
2142 << params.paperwidth;
2143 if (!params.paperheight.empty())
2144 os << ",paperheight="
2145 << params.paperheight;
2147 case BufferParams::VM_PAPER_USLETTER:
2148 os << ",letterpaper";
2150 case BufferParams::VM_PAPER_USLEGAL:
2151 os << ",legalpaper";
2153 case BufferParams::VM_PAPER_USEXECUTIVE:
2154 os << ",executivepaper";
2156 case BufferParams::VM_PAPER_A3:
2159 case BufferParams::VM_PAPER_A4:
2162 case BufferParams::VM_PAPER_A5:
2165 case BufferParams::VM_PAPER_B3:
2168 case BufferParams::VM_PAPER_B4:
2171 case BufferParams::VM_PAPER_B5:
2175 // default papersize ie BufferParams::VM_PAPER_DEFAULT
2176 switch (lyxrc.default_papersize) {
2177 case BufferParams::PAPER_DEFAULT: // keep compiler happy
2178 case BufferParams::PAPER_USLETTER:
2179 os << ",letterpaper";
2181 case BufferParams::PAPER_LEGALPAPER:
2182 os << ",legalpaper";
2184 case BufferParams::PAPER_EXECUTIVEPAPER:
2185 os << ",executivepaper";
2187 case BufferParams::PAPER_A3PAPER:
2190 case BufferParams::PAPER_A4PAPER:
2193 case BufferParams::PAPER_A5PAPER:
2196 case BufferParams::PAPER_B5PAPER:
2201 if (!params.topmargin.empty())
2202 os << ",tmargin=" << params.topmargin;
2203 if (!params.bottommargin.empty())
2204 os << ",bmargin=" << params.bottommargin;
2205 if (!params.leftmargin.empty())
2206 os << ",lmargin=" << params.leftmargin;
2207 if (!params.rightmargin.empty())
2208 os << ",rmargin=" << params.rightmargin;
2209 if (!params.headheight.empty())
2210 os << ",headheight=" << params.headheight;
2211 if (!params.headsep.empty())
2212 os << ",headsep=" << params.headsep;
2213 if (!params.footskip.empty())
2214 os << ",footskip=" << params.footskip;
2219 if (tokenPos(tclass.opt_pagestyle(),
2220 '|', params.pagestyle) >= 0) {
2221 if (params.pagestyle == "fancy") {
2222 os << "\\usepackage{fancyhdr}\n";
2225 os << "\\pagestyle{" << params.pagestyle << "}\n";
2229 if (params.secnumdepth != tclass.secnumdepth()) {
2230 os << "\\setcounter{secnumdepth}{"
2231 << params.secnumdepth
2235 if (params.tocdepth != tclass.tocdepth()) {
2236 os << "\\setcounter{tocdepth}{"
2242 if (params.paragraph_separation) {
2243 switch (params.defskip.kind()) {
2244 case VSpace::SMALLSKIP:
2245 os << "\\setlength\\parskip{\\smallskipamount}\n";
2247 case VSpace::MEDSKIP:
2248 os << "\\setlength\\parskip{\\medskipamount}\n";
2250 case VSpace::BIGSKIP:
2251 os << "\\setlength\\parskip{\\bigskipamount}\n";
2253 case VSpace::LENGTH:
2254 os << "\\setlength\\parskip{"
2255 << params.defskip.length().asLatexString()
2258 default: // should never happen // Then delete it.
2259 os << "\\setlength\\parskip{\\medskipamount}\n";
2264 os << "\\setlength\\parindent{0pt}\n";
2268 // Now insert the LyX specific LaTeX commands...
2270 // The optional packages;
2271 string preamble(features.getPackages());
2273 // this might be useful...
2274 preamble += "\n\\makeatletter\n";
2276 // Some macros LyX will need
2277 string tmppreamble(features.getMacros());
2279 if (!tmppreamble.empty()) {
2280 preamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2281 "LyX specific LaTeX commands.\n"
2282 + tmppreamble + '\n';
2285 // the text class specific preamble
2286 tmppreamble = features.getTClassPreamble();
2287 if (!tmppreamble.empty()) {
2288 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2289 "Textclass specific LaTeX commands.\n"
2290 + tmppreamble + '\n';
2293 /* the user-defined preamble */
2294 if (!params.preamble.empty()) {
2295 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2296 "User specified LaTeX commands.\n"
2297 + params.preamble + '\n';
2300 // Itemize bullet settings need to be last in case the user
2301 // defines their own bullets that use a package included
2302 // in the user-defined preamble -- ARRae
2303 // Actually it has to be done much later than that
2304 // since some packages like frenchb make modifications
2305 // at \begin{document} time -- JMarc
2307 for (int i = 0; i < 4; ++i) {
2308 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
2309 if (bullets_def.empty())
2310 bullets_def="\\AtBeginDocument{\n";
2311 bullets_def += " \\renewcommand{\\labelitemi";
2313 // `i' is one less than the item to modify
2320 bullets_def += "ii";
2326 bullets_def += "}{" +
2327 params.user_defined_bullets[i].getText()
2332 if (!bullets_def.empty())
2333 preamble += bullets_def + "}\n\n";
2336 int(lyx::count(preamble.begin(), preamble.end(), '\n'));
2337 for (int j = 0; j != nlines; ++j) {
2341 // We try to load babel late, in case it interferes
2342 // with other packages.
2344 string tmp = lyxrc.language_package;
2345 if (!lyxrc.language_global_options
2346 && tmp == "\\usepackage{babel}")
2347 tmp = string("\\usepackage[") +
2348 language_options.str().c_str() +
2350 preamble += tmp + "\n";
2351 preamble += features.getBabelOptions();
2354 preamble += "\\makeatother\n";
2362 os << "\\begin{document}\n";
2365 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
2367 if (!lyxrc.language_auto_begin) {
2368 os << subst(lyxrc.language_command_begin, "$$lang",
2369 params.language->babel())
2374 latexParagraphs(os, &*(paragraphs.begin()), 0, texrow);
2376 // add this just in case after all the paragraphs
2380 if (!lyxrc.language_auto_end) {
2381 os << subst(lyxrc.language_command_end, "$$lang",
2382 params.language->babel())
2388 os << "\\end{document}\n";
2391 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
2393 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
2397 // Just to be sure. (Asger)
2400 lyxerr[Debug::INFO] << "Finished making latex file." << endl;
2401 lyxerr[Debug::INFO] << "Row count was " << texrow.rows()-1 << "." << endl;
2403 // we want this to be true outside previews (for insetexternal)
2409 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
2411 void Buffer::latexParagraphs(ostream & ofs, Paragraph * par,
2412 Paragraph * endpar, TexRow & texrow,
2413 bool moving_arg) const
2415 bool was_title = false;
2416 bool already_title = false;
2419 while (par != endpar) {
2420 Inset * in = par->inInset();
2421 // well we have to check if we are in an inset with unlimited
2422 // length (all in one row) if that is true then we don't allow
2423 // any special options in the paragraph and also we don't allow
2424 // any environment other then "Standard" to be valid!
2425 if ((in == 0) || !in->forceDefaultParagraphs(in)) {
2426 LyXLayout_ptr const & layout = par->layout();
2428 if (layout->intitle) {
2429 if (already_title) {
2430 lyxerr <<"Error in latexParagraphs: You"
2431 " should not mix title layouts"
2432 " with normal ones." << endl;
2435 } else if (was_title && !already_title) {
2436 ofs << "\\maketitle\n";
2438 already_title = true;
2442 if (layout->isEnvironment() ||
2443 !par->params().leftIndent().zero())
2445 par = par->TeXEnvironment(this, params, ofs, texrow);
2447 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2450 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2453 // It might be that we only have a title in this document
2454 if (was_title && !already_title) {
2455 ofs << "\\maketitle\n";
2461 bool Buffer::isLatex() const
2463 return params.getLyXTextClass().outputType() == LATEX;
2467 bool Buffer::isLinuxDoc() const
2469 return params.getLyXTextClass().outputType() == LINUXDOC;
2473 bool Buffer::isLiterate() const
2475 return params.getLyXTextClass().outputType() == LITERATE;
2479 bool Buffer::isDocBook() const
2481 return params.getLyXTextClass().outputType() == DOCBOOK;
2485 bool Buffer::isSGML() const
2487 LyXTextClass const & tclass = params.getLyXTextClass();
2489 return tclass.outputType() == LINUXDOC ||
2490 tclass.outputType() == DOCBOOK;
2494 int Buffer::sgmlOpenTag(ostream & os, Paragraph::depth_type depth, bool mixcont,
2495 string const & latexname) const
2497 if (!latexname.empty() && latexname != "!-- --") {
2499 os << string(" ",depth);
2500 os << "<" << latexname << ">";
2510 int Buffer::sgmlCloseTag(ostream & os, Paragraph::depth_type depth, bool mixcont,
2511 string const & latexname) const
2513 if (!latexname.empty() && latexname != "!-- --") {
2515 os << endl << string(" ",depth);
2516 os << "</" << latexname << ">";
2526 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
2528 ofstream ofs(fname.c_str());
2531 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2535 niceFile = nice; // this will be used by included files.
2537 LaTeXFeatures features(params);
2543 LyXTextClass const & tclass = params.getLyXTextClass();
2545 string top_element = tclass.latexname();
2548 ofs << "<!doctype linuxdoc system";
2550 string preamble = params.preamble;
2551 const string name = nice ? ChangeExtension(filename_, ".sgml")
2553 preamble += features.getIncludedFiles(name);
2554 preamble += features.getLyXSGMLEntities();
2556 if (!preamble.empty()) {
2557 ofs << " [ " << preamble << " ]";
2561 if (params.options.empty())
2562 sgmlOpenTag(ofs, 0, false, top_element);
2564 string top = top_element;
2566 top += params.options;
2567 sgmlOpenTag(ofs, 0, false, top);
2571 ofs << "<!-- " << lyx_docversion
2572 << " created this file. For more info see http://www.lyx.org/"
2575 Paragraph::depth_type depth = 0; // paragraph depth
2576 Paragraph * par = &*(paragraphs.begin());
2578 vector<string> environment_stack(5);
2581 LyXLayout_ptr const & style = par->layout();
2582 // treat <toc> as a special case for compatibility with old code
2583 if (par->isInset(0)) {
2584 Inset * inset = par->getInset(0);
2585 Inset::Code lyx_code = inset->lyxCode();
2586 if (lyx_code == Inset::TOC_CODE) {
2587 string const temp = "toc";
2588 sgmlOpenTag(ofs, depth, false, temp);
2595 // environment tag closing
2596 for (; depth > par->params().depth(); --depth) {
2597 sgmlCloseTag(ofs, depth, false, environment_stack[depth]);
2598 environment_stack[depth].erase();
2601 // write opening SGML tags
2602 switch (style->latextype) {
2603 case LATEX_PARAGRAPH:
2604 if (depth == par->params().depth()
2605 && !environment_stack[depth].empty()) {
2606 sgmlCloseTag(ofs, depth, false, environment_stack[depth]);
2607 environment_stack[depth].erase();
2613 sgmlOpenTag(ofs, depth, false, style->latexname());
2619 _("Error : Wrong depth for"
2620 " LatexType Command.\n"));
2622 if (!environment_stack[depth].empty()) {
2623 sgmlCloseTag(ofs, depth, false, environment_stack[depth]);
2627 environment_stack[depth].erase();
2628 sgmlOpenTag(ofs, depth, false, style->latexname());
2631 case LATEX_ENVIRONMENT:
2632 case LATEX_ITEM_ENVIRONMENT:
2634 string const & latexname = style->latexname();
2636 if (depth == par->params().depth()
2637 && environment_stack[depth] != latexname) {
2638 sgmlCloseTag(ofs, depth, false,
2639 environment_stack[depth]);
2640 environment_stack[depth].erase();
2642 if (depth < par->params().depth()) {
2643 depth = par->params().depth();
2644 environment_stack[depth].erase();
2646 if (environment_stack[depth] != latexname) {
2648 sgmlOpenTag(ofs, depth, false, "p");
2650 sgmlOpenTag(ofs, depth, false, latexname);
2652 if (environment_stack.size() == depth + 1)
2653 environment_stack.push_back("!-- --");
2654 environment_stack[depth] = latexname;
2657 if (style->latexparam() == "CDATA")
2660 if (style->latextype == LATEX_ENVIRONMENT) break;
2662 if (style->labeltype == LABEL_MANUAL)
2667 sgmlOpenTag(ofs, depth + 1, false, item_name);
2672 sgmlOpenTag(ofs, depth, false, style->latexname());
2676 simpleLinuxDocOnePar(ofs, par, depth);
2681 // write closing SGML tags
2682 switch (style->latextype) {
2685 case LATEX_ENVIRONMENT:
2686 case LATEX_ITEM_ENVIRONMENT:
2687 if (style->latexparam() == "CDATA")
2691 sgmlCloseTag(ofs, depth, false, style->latexname());
2697 for (int i = depth; i >= 0; --i)
2698 sgmlCloseTag(ofs, depth, false, environment_stack[i]);
2702 sgmlCloseTag(ofs, 0, false, top_element);
2706 // How to check for successful close
2708 // we want this to be true outside previews (for insetexternal)
2713 // checks, if newcol chars should be put into this line
2714 // writes newline, if necessary.
2717 void sgmlLineBreak(ostream & os, string::size_type & colcount,
2718 string::size_type newcol)
2721 if (colcount > lyxrc.ascii_linelen) {
2723 colcount = newcol; // assume write after this call
2738 string tag_name(PAR_TAG const & pt) {
2740 case NONE: return "!-- --";
2741 case TT: return "tt";
2742 case SF: return "sf";
2743 case BF: return "bf";
2744 case IT: return "it";
2745 case SL: return "sl";
2746 case EM: return "em";
2753 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2755 p1 = static_cast<PAR_TAG>(p1 | p2);
2760 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2762 p1 = static_cast<PAR_TAG>(p1 & ~p2);
2768 // Handle internal paragraph parsing -- layout already processed.
2769 void Buffer::simpleLinuxDocOnePar(ostream & os,
2771 Paragraph::depth_type /*depth*/)
2773 LyXLayout_ptr const & style = par->layout();
2775 string::size_type char_line_count = 5; // Heuristic choice ;-)
2777 // gets paragraph main font
2780 if (style->labeltype == LABEL_MANUAL) {
2781 font_old = style->labelfont;
2784 font_old = style->font;
2788 LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
2789 LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
2790 LyXFont::FONT_SHAPE shape_type = LyXFont::UP_SHAPE;
2793 stack<PAR_TAG> tag_state;
2794 // parsing main loop
2795 for (pos_type i = 0; i < par->size(); ++i) {
2797 PAR_TAG tag_close = NONE;
2798 list < PAR_TAG > tag_open;
2800 LyXFont const font = par->getFont(params, i);
2802 if (font_old.family() != font.family()) {
2803 switch (family_type) {
2804 case LyXFont::SANS_FAMILY:
2807 case LyXFont::TYPEWRITER_FAMILY:
2814 family_type = font.family();
2816 switch (family_type) {
2817 case LyXFont::SANS_FAMILY:
2818 tag_open.push_back(SF);
2820 case LyXFont::TYPEWRITER_FAMILY:
2821 tag_open.push_back(TT);
2828 if (font_old.series() != font.series()) {
2829 switch (series_type) {
2830 case LyXFont::BOLD_SERIES:
2837 series_type = font.series();
2839 switch (series_type) {
2840 case LyXFont::BOLD_SERIES:
2841 tag_open.push_back(BF);
2849 if (font_old.shape() != font.shape()) {
2850 switch (shape_type) {
2851 case LyXFont::ITALIC_SHAPE:
2854 case LyXFont::SLANTED_SHAPE:
2861 shape_type = font.shape();
2863 switch (shape_type) {
2864 case LyXFont::ITALIC_SHAPE:
2865 tag_open.push_back(IT);
2867 case LyXFont::SLANTED_SHAPE:
2868 tag_open.push_back(SL);
2875 if (font_old.emph() != font.emph()) {
2876 if (font.emph() == LyXFont::ON) {
2877 tag_open.push_back(EM);
2886 list < PAR_TAG > temp;
2887 while (!tag_state.empty() && tag_close) {
2888 PAR_TAG k = tag_state.top();
2890 os << "</" << tag_name(k) << ">";
2897 for(list< PAR_TAG >::const_iterator j = temp.begin();
2898 j != temp.end(); ++j) {
2900 os << "<" << tag_name(*j) << ">";
2903 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
2904 j != tag_open.end(); ++j) {
2906 os << "<" << tag_name(*j) << ">";
2909 char c = par->getChar(i);
2911 if (c == Paragraph::META_INSET) {
2912 Inset * inset = par->getInset(i);
2913 inset->linuxdoc(this, os);
2918 if (style->latexparam() == "CDATA") {
2919 // "TeX"-Mode on == > SGML-Mode on.
2926 boost::tie(ws, str) = sgml::escapeChar(c);
2927 if (ws && !style->free_spacing && !par->isFreeSpacing()) {
2928 // in freespacing mode, spaces are
2929 // non-breaking characters
2930 if (desc_on) {// if char is ' ' then...
2933 sgmlLineBreak(os, char_line_count, 6);
2937 sgmlLineBreak(os, char_line_count, 1);
2942 char_line_count += str.length();
2948 while (!tag_state.empty()) {
2949 os << "</" << tag_name(tag_state.top()) << ">";
2953 // resets description flag correctly
2955 // <tag> not closed...
2956 sgmlLineBreak(os, char_line_count, 6);
2962 // Print an error message.
2963 void Buffer::sgmlError(Paragraph * /*par*/, int /*pos*/,
2964 string const & /*message*/) const
2966 #ifdef WITH_WARNINGS
2967 #warning This is wrong we cannot insert an inset like this!!!
2968 // I guess this was Jose' so I explain you more or less why this
2969 // is wrong. This way you insert something in the paragraph and
2970 // don't tell it to LyXText (row rebreaking and undo handling!!!)
2971 // I deactivate this code, have a look at BufferView::insertErrors
2972 // how you should do this correctly! (Jug 20020315)
2975 // insert an error marker in text
2976 InsetError * new_inset = new InsetError(message);
2977 par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
2983 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
2985 ofstream ofs(fname.c_str());
2987 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2991 Paragraph * par = &*(paragraphs.begin());
2993 niceFile = nice; // this will be used by Insetincludes.
2995 LaTeXFeatures features(params);
3000 LyXTextClass const & tclass = params.getLyXTextClass();
3001 string top_element = tclass.latexname();
3004 ofs << "<!DOCTYPE " << top_element
3005 << " PUBLIC \"-//OASIS//DTD DocBook V4.1//EN\"";
3007 string preamble = params.preamble;
3008 const string name = nice ? ChangeExtension(filename_, ".sgml")
3010 preamble += features.getIncludedFiles(name);
3011 preamble += features.getLyXSGMLEntities();
3013 if (!preamble.empty()) {
3014 ofs << "\n [ " << preamble << " ]";
3019 string top = top_element;
3021 top += params.language->code();
3024 if (!params.options.empty()) {
3026 top += params.options;
3028 sgmlOpenTag(ofs, 0, false, top);
3030 ofs << "<!-- DocBook file was created by " << lyx_docversion
3031 << "\n See http://www.lyx.org/ for more information -->\n";
3033 vector<string> environment_stack(10);
3034 vector<string> environment_inner(10);
3035 vector<string> command_stack(10);
3037 bool command_flag = false;
3038 Paragraph::depth_type command_depth = 0;
3039 Paragraph::depth_type command_base = 0;
3040 Paragraph::depth_type cmd_depth = 0;
3041 Paragraph::depth_type depth = 0; // paragraph depth
3044 string command_name;
3050 int desc_on = 0; // description mode
3052 LyXLayout_ptr const & style = par->layout();
3054 // environment tag closing
3055 for (; depth > par->params().depth(); --depth) {
3056 if (environment_inner[depth] != "!-- --") {
3057 item_name = "listitem";
3058 sgmlCloseTag(ofs, command_depth + depth, false, item_name);
3059 if (environment_inner[depth] == "varlistentry")
3060 sgmlCloseTag(ofs, depth+command_depth, false, environment_inner[depth]);
3062 sgmlCloseTag(ofs, depth + command_depth, false, environment_stack[depth]);
3063 environment_stack[depth].erase();
3064 environment_inner[depth].erase();
3067 if (depth == par->params().depth()
3068 && environment_stack[depth] != style->latexname()
3069 && !environment_stack[depth].empty()) {
3070 if (environment_inner[depth] != "!-- --") {
3071 item_name= "listitem";
3072 sgmlCloseTag(ofs, command_depth+depth, false, item_name);
3073 if (environment_inner[depth] == "varlistentry")
3074 sgmlCloseTag(ofs, depth + command_depth, false, environment_inner[depth]);
3077 sgmlCloseTag(ofs, depth + command_depth, false, environment_stack[depth]);
3079 environment_stack[depth].erase();
3080 environment_inner[depth].erase();
3083 // Write opening SGML tags.
3084 switch (style->latextype) {
3085 case LATEX_PARAGRAPH:
3086 sgmlOpenTag(ofs, depth + command_depth,
3087 false, style->latexname());
3093 _("Error : Wrong depth for "
3094 "LatexType Command.\n"));
3096 command_name = style->latexname();
3098 sgmlparam = style->latexparam();
3099 c_params = split(sgmlparam, c_depth,'|');
3101 cmd_depth = lyx::atoi(c_depth);
3104 if (cmd_depth < command_base) {
3105 for (Paragraph::depth_type j = command_depth;
3106 j >= command_base; --j) {
3107 sgmlCloseTag(ofs, j, false, command_stack[j]);
3110 command_depth = command_base = cmd_depth;
3111 } else if (cmd_depth <= command_depth) {
3112 for (int j = command_depth;
3113 j >= int(cmd_depth); --j) {
3114 sgmlCloseTag(ofs, j, false, command_stack[j]);
3117 command_depth = cmd_depth;
3119 command_depth = cmd_depth;
3121 command_depth = command_base = cmd_depth;
3122 command_flag = true;
3124 if (command_stack.size() == command_depth + 1)
3125 command_stack.push_back(string());
3126 command_stack[command_depth] = command_name;
3128 // treat label as a special case for
3129 // more WYSIWYM handling.
3130 // This is a hack while paragraphs can't have
3131 // attributes, like id in this case.
3132 if (par->isInset(0)) {
3133 Inset * inset = par->getInset(0);
3134 Inset::Code lyx_code = inset->lyxCode();
3135 if (lyx_code == Inset::LABEL_CODE) {
3136 command_name += " id=\"";
3137 command_name += (static_cast<InsetCommand *>(inset))->getContents();
3138 command_name += "\"";
3143 sgmlOpenTag(ofs, depth + command_depth, false, command_name);
3145 item_name = c_params.empty()?"title":c_params;
3146 sgmlOpenTag(ofs, depth + 1 + command_depth, false, item_name);
3149 case LATEX_ENVIRONMENT:
3150 case LATEX_ITEM_ENVIRONMENT:
3151 if (depth < par->params().depth()) {
3152 depth = par->params().depth();
3153 environment_stack[depth].erase();
3156 if (environment_stack[depth] != style->latexname()) {
3157 if (environment_stack.size() == depth + 1) {
3158 environment_stack.push_back("!-- --");
3159 environment_inner.push_back("!-- --");
3161 environment_stack[depth] = style->latexname();
3162 environment_inner[depth] = "!-- --";
3163 sgmlOpenTag(ofs, depth + command_depth, false, environment_stack[depth]);
3165 if (environment_inner[depth] != "!-- --") {
3166 item_name= "listitem";
3167 sgmlCloseTag(ofs, command_depth + depth, false, item_name);
3168 if (environment_inner[depth] == "varlistentry")
3169 sgmlCloseTag(ofs, depth + command_depth, false, environment_inner[depth]);
3173 if (style->latextype == LATEX_ENVIRONMENT) {
3174 if (!style->latexparam().empty()) {
3175 if (style->latexparam() == "CDATA")
3178 sgmlOpenTag(ofs, depth + command_depth, false, style->latexparam());
3183 desc_on = (style->labeltype == LABEL_MANUAL);
3185 environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
3186 sgmlOpenTag(ofs, depth + 1 + command_depth,
3187 false, environment_inner[depth]);
3189 item_name = desc_on ? "term" : "para";
3190 sgmlOpenTag(ofs, depth + 1 + command_depth,
3194 sgmlOpenTag(ofs, depth + command_depth,
3195 false, style->latexname());
3199 simpleDocBookOnePar(ofs, par, desc_on,
3200 depth + 1 + command_depth);
3204 // write closing SGML tags
3205 switch (style->latextype) {
3207 end_tag = c_params.empty() ? "title" : c_params;
3208 sgmlCloseTag(ofs, depth + command_depth,
3211 case LATEX_ENVIRONMENT:
3212 if (!style->latexparam().empty()) {
3213 if (style->latexparam() == "CDATA")
3216 sgmlCloseTag(ofs, depth + command_depth, false, style->latexparam());
3219 case LATEX_ITEM_ENVIRONMENT:
3220 if (desc_on == 1) break;
3222 sgmlCloseTag(ofs, depth + 1 + command_depth, false, end_tag);
3224 case LATEX_PARAGRAPH:
3225 sgmlCloseTag(ofs, depth + command_depth, false, style->latexname());
3228 sgmlCloseTag(ofs, depth + command_depth, false, style->latexname());
3234 for (int d = depth; d >= 0; --d) {
3235 if (!environment_stack[depth].empty()) {
3236 if (environment_inner[depth] != "!-- --") {
3237 item_name = "listitem";
3238 sgmlCloseTag(ofs, command_depth + depth, false, item_name);
3239 if (environment_inner[depth] == "varlistentry")
3240 sgmlCloseTag(ofs, depth + command_depth, false, environment_inner[depth]);
3243 sgmlCloseTag(ofs, depth + command_depth, false, environment_stack[depth]);
3247 for (int j = command_depth; j >= 0 ; --j)
3248 if (!command_stack[j].empty()) {
3249 sgmlCloseTag(ofs, j, false, command_stack[j]);
3254 sgmlCloseTag(ofs, 0, false, top_element);
3257 // How to check for successful close
3259 // we want this to be true outside previews (for insetexternal)
3264 void Buffer::simpleDocBookOnePar(ostream & os,
3265 Paragraph * par, int & desc_on,
3266 Paragraph::depth_type depth) const
3268 bool emph_flag = false;
3270 LyXLayout_ptr const & style = par->layout();
3272 LyXFont font_old = (style->labeltype == LABEL_MANUAL ? style->labelfont : style->font);
3274 int char_line_count = depth;
3275 //if (!style.free_spacing)
3276 // os << string(depth,' ');
3278 // parsing main loop
3279 for (pos_type i = 0; i < par->size(); ++i) {
3280 LyXFont font = par->getFont(params, i);
3282 // handle <emphasis> tag
3283 if (font_old.emph() != font.emph()) {
3284 if (font.emph() == LyXFont::ON) {
3285 if (style->latexparam() == "CDATA")
3288 if (style->latexparam() == "CDATA")
3292 if (style->latexparam() == "CDATA")
3294 os << "</emphasis>";
3295 if (style->latexparam() == "CDATA")
3302 if (par->isInset(i)) {
3303 Inset * inset = par->getInset(i);
3304 // don't print the inset in position 0 if desc_on == 3 (label)
3305 if (i || desc_on != 3) {
3306 if (style->latexparam() == "CDATA")
3308 inset->docbook(this, os, false);
3309 if (style->latexparam() == "CDATA")
3313 char c = par->getChar(i);
3316 boost::tie(ws, str) = sgml::escapeChar(c);
3318 if (style->pass_thru) {
3320 } else if (style->free_spacing || par->isFreeSpacing() || c != ' ') {
3322 } else if (desc_on ==1) {
3324 os << "\n</term><listitem><para>";
3334 if (style->latexparam() == "CDATA")
3336 os << "</emphasis>";
3337 if (style->latexparam() == "CDATA")
3341 // resets description flag correctly
3343 // <term> not closed...
3344 os << "</term>\n<listitem><para> </para>";
3346 if (style->free_spacing)
3351 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
3352 // Other flags: -wall -v0 -x
3353 int Buffer::runChktex()
3355 if (!users->text) return 0;
3357 users->owner()->prohibitInput();
3359 // get LaTeX-Filename
3360 string const name = getLatexName();
3361 string path = filePath();
3363 string const org_path = path;
3364 if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
3368 Path p(path); // path to LaTeX file
3369 users->owner()->message(_("Running chktex..."));
3371 // Remove all error insets
3372 bool const removedErrorInsets = users->removeAutoInsets();
3374 // Generate the LaTeX file if neccessary
3375 makeLaTeXFile(name, org_path, false);
3378 Chktex chktex(lyxrc.chktex_command, name, filePath());
3379 int res = chktex.run(terr); // run chktex
3382 Alert::alert(_("chktex did not work!"),
3383 _("Could not run with file:"), name);
3384 } else if (res > 0) {
3385 // Insert all errors as errors boxes
3386 users->insertErrors(terr);
3389 // if we removed error insets before we ran chktex or if we inserted
3390 // error insets after we ran chktex, this must be run:
3391 if (removedErrorInsets || res) {
3392 #warning repaint needed here, or do you mean update() ?
3396 users->owner()->allowInput();
3402 void Buffer::validate(LaTeXFeatures & features) const
3404 LyXTextClass const & tclass = params.getLyXTextClass();
3406 // AMS Style is at document level
3407 if (params.use_amsmath || tclass.provides(LyXTextClass::amsmath))
3408 features.require("amsmath");
3410 for_each(paragraphs.begin(), paragraphs.end(),
3411 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
3413 // the bullet shapes are buffer level not paragraph level
3414 // so they are tested here
3415 for (int i = 0; i < 4; ++i) {
3416 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
3417 int const font = params.user_defined_bullets[i].getFont();
3419 int const c = params
3420 .user_defined_bullets[i]
3427 features.require("latexsym");
3429 } else if (font == 1) {
3430 features.require("amssymb");
3431 } else if ((font >= 2 && font <= 5)) {
3432 features.require("pifont");
3437 if (lyxerr.debugging(Debug::LATEX)) {
3438 features.showStruct();
3443 // This function should be in Buffer because it's a buffer's property (ale)
3444 string const Buffer::getIncludeonlyList(char delim)
3447 for (inset_iterator it = inset_iterator_begin();
3448 it != inset_iterator_end(); ++it) {
3449 if (it->lyxCode() == Inset::INCLUDE_CODE) {
3450 InsetInclude & inc = static_cast<InsetInclude &>(*it);
3451 if (inc.isIncludeOnly()) {
3454 lst += inc.getRelFileBaseName();
3458 lyxerr[Debug::INFO] << "Includeonly(" << lst << ')' << endl;
3463 vector<string> const Buffer::getLabelList() const
3465 /// if this is a child document and the parent is already loaded
3466 /// Use the parent's list instead [ale990407]
3467 if (!params.parentname.empty()
3468 && bufferlist.exists(params.parentname)) {
3469 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
3471 return tmp->getLabelList();
3474 vector<string> label_list;
3475 for (inset_iterator it = inset_const_iterator_begin();
3476 it != inset_const_iterator_end(); ++it) {
3477 vector<string> const l = it->getLabelList();
3478 label_list.insert(label_list.end(), l.begin(), l.end());
3484 // This is also a buffer property (ale)
3485 vector<pair<string, string> > const Buffer::getBibkeyList() const
3487 typedef pair<string, string> StringPair;
3488 /// if this is a child document and the parent is already loaded
3489 /// Use the parent's list instead [ale990412]
3490 if (!params.parentname.empty() && bufferlist.exists(params.parentname)) {
3491 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
3493 return tmp->getBibkeyList();
3496 vector<StringPair> keys;
3497 ParagraphList::iterator pit = paragraphs.begin();
3498 ParagraphList::iterator pend = paragraphs.end();
3499 for (; pit != pend; ++pit) {
3501 string const key = pit->bibkey->getContents();
3502 string const opt = pit->bibkey->getOptions();
3503 string const ref = pit->asString(this, false);
3504 string const info = opt + "TheBibliographyRef" + ref;
3506 keys.push_back(StringPair(key, info));
3510 // Might be either using bibtex or a child has bibliography
3512 for (inset_iterator it = inset_const_iterator_begin();
3513 it != inset_const_iterator_end(); ++it) {
3514 // Search for Bibtex or Include inset
3515 if (it->lyxCode() == Inset::BIBTEX_CODE) {
3516 vector<StringPair> tmp =
3517 static_cast<InsetBibtex &>(*it).getKeys(this);
3518 keys.insert(keys.end(), tmp.begin(), tmp.end());
3519 } else if (it->lyxCode() == Inset::INCLUDE_CODE) {
3520 vector<StringPair> const tmp =
3521 static_cast<InsetInclude &>(*it).getKeys();
3522 keys.insert(keys.end(), tmp.begin(), tmp.end());
3531 bool Buffer::isDepClean(string const & name) const
3533 DEPCLEAN * item = dep_clean;
3534 while (item && item->master != name)
3536 if (!item) return true;
3541 void Buffer::markDepClean(string const & name)
3544 dep_clean = new DEPCLEAN;
3545 dep_clean->clean = true;
3546 dep_clean->master = name;
3547 dep_clean->next = 0;
3549 DEPCLEAN * item = dep_clean;
3550 while (item && item->master != name)
3555 item = new DEPCLEAN;
3557 item->master = name;
3564 bool Buffer::dispatch(string const & command, bool * result)
3566 // Split command string into command and argument
3568 string line = ltrim(command);
3569 string const arg = trim(split(line, cmd, ' '));
3571 return dispatch(lyxaction.LookupFunc(cmd), arg, result);
3575 bool Buffer::dispatch(int action, string const & argument, bool * result)
3577 bool dispatched = true;
3581 bool const tmp = Exporter::Export(this, argument, false);
3594 void Buffer::resizeInsets(BufferView * bv)
3596 /// then remove all LyXText in text-insets
3597 for_each(paragraphs.begin(), paragraphs.end(),
3598 boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
3602 void Buffer::redraw()
3604 #warning repaint needed here, or do you mean update() ?
3610 void Buffer::changeLanguage(Language const * from, Language const * to)
3613 ParIterator end = par_iterator_end();
3614 for (ParIterator it = par_iterator_begin(); it != end; ++it)
3615 (*it)->changeLanguage(params, from, to);
3619 bool Buffer::isMultiLingual()
3621 ParIterator end = par_iterator_end();
3622 for (ParIterator it = par_iterator_begin(); it != end; ++it)
3623 if ((*it)->isMultiLingual(params))
3630 Counters & Buffer::counters() const
3636 void Buffer::inset_iterator::setParagraph()
3638 while (pit != pend) {
3639 it = pit->insetlist.begin();
3640 if (it != pit->insetlist.end())
3647 Inset * Buffer::getInsetFromID(int id_arg) const
3649 for (inset_iterator it = inset_const_iterator_begin();
3650 it != inset_const_iterator_end(); ++it)
3652 if (it->id() == id_arg)
3654 Inset * in = it->getInsetFromID(id_arg);
3662 Paragraph * Buffer::getParFromID(int id) const
3667 ParagraphList::iterator it = paragraphs.begin();
3668 ParagraphList::iterator end = paragraphs.end();
3669 for (; it != end; ++it) {
3670 if (it->id() == id) {
3673 Paragraph * tmp = it->getParFromID(id);
3682 ParIterator Buffer::par_iterator_begin()
3684 return ParIterator(&*(paragraphs.begin()));
3688 ParIterator Buffer::par_iterator_end()
3690 return ParIterator();
3694 void Buffer::addUser(BufferView * u)
3700 void Buffer::delUser(BufferView *)
3706 Language const * Buffer::getLanguage() const
3708 return params.language;
3712 bool Buffer::isClean() const
3718 bool Buffer::isBakClean() const
3724 void Buffer::markClean() const
3730 // if the .lyx file has been saved, we don't need an
3736 void Buffer::markBakClean()
3742 void Buffer::setUnnamed(bool flag)
3748 bool Buffer::isUnnamed()
3754 void Buffer::markDirty()
3761 DEPCLEAN * tmp = dep_clean;
3769 string const & Buffer::fileName() const
3775 string const & Buffer::filePath() const
3781 bool Buffer::isReadonly() const
3787 BufferView * Buffer::getUser() const
3793 void Buffer::setParentName(string const & name)
3795 params.parentname = name;
3799 Buffer::inset_iterator::inset_iterator()
3804 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
3811 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
3815 if (it == pit->insetlist.end()) {
3824 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
3826 inset_iterator tmp = *this;
3832 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
3834 return *it.getInset();
3838 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
3840 return it.getInset();
3844 Paragraph * Buffer::inset_iterator::getPar()
3850 lyx::pos_type Buffer::inset_iterator::getPos() const
3856 bool operator==(Buffer::inset_iterator const & iter1,
3857 Buffer::inset_iterator const & iter2)
3859 return iter1.pit == iter2.pit
3860 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
3864 bool operator!=(Buffer::inset_iterator const & iter1,
3865 Buffer::inset_iterator const & iter2)
3867 return !(iter1 == iter2);