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 * ======================================================
26 #include <sys/types.h>
36 #pragma implementation
40 #include "bufferlist.h"
42 #include "lyx_gui_misc.h"
43 #include "LyXAction.h"
46 #include "tex-strings.h"
48 #include "bufferview_funcs.h"
51 #include "mathed/formulamacro.h"
52 #include "mathed/formula.h"
53 #include "insets/inset.h"
54 #include "insets/inseterror.h"
55 #include "insets/insetlabel.h"
56 #include "insets/insetref.h"
57 #include "insets/inseturl.h"
58 #include "insets/insetinfo.h"
59 #include "insets/insetquotes.h"
60 #include "insets/insetlatexaccent.h"
61 #include "insets/insetbib.h"
62 #include "insets/insetcite.h"
63 #include "insets/insetexternal.h"
64 #include "insets/insetindex.h"
65 #include "insets/insetinclude.h"
66 #include "insets/insettoc.h"
67 #include "insets/insetparent.h"
68 #include "insets/insetspecialchar.h"
69 #include "insets/figinset.h"
70 #include "insets/insettext.h"
71 #include "insets/insetert.h"
72 #include "insets/insetgraphics.h"
73 #include "insets/insetfoot.h"
74 #include "insets/insetmarginal.h"
75 #include "insets/insetminipage.h"
76 #include "insets/insetfloat.h"
77 #include "insets/insetlist.h"
78 #include "insets/insettabular.h"
79 #include "insets/insettheorem.h"
80 #include "insets/insetcaption.h"
81 #include "insets/insetfloatlist.h"
82 #include "support/filetools.h"
83 #include "support/path.h"
84 #include "support/os.h"
89 #include "LaTeXFeatures.h"
90 #include "support/syscall.h"
91 #include "support/lyxlib.h"
92 #include "support/FileInfo.h"
93 #include "support/lyxmanip.h"
97 #include "lyx_gui_misc.h" // WarnReadonly()
98 #include "frontends/Dialogs.h"
100 #include "exporter.h"
101 #include "Lsstream.h"
102 #include "converter.h"
103 #include "BufferView.h"
104 #include "ParagraphParameters.h"
106 using std::stringstream;
115 using std::make_pair;
123 // all these externs should eventually be removed.
124 extern BufferList bufferlist;
126 extern LyXAction lyxaction;
130 const int LYX_FORMAT = 220;
134 extern int tex_code_break_column;
137 Buffer::Buffer(string const & file, bool ronly)
139 lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
141 filepath = OnlyPath(file);
150 if (read_only || (lyxrc.use_tempdir)) {
151 tmppath = CreateBufferTmpDir();
152 } else tmppath.erase();
158 lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
159 // here the buffer should take care that it is
160 // saved properly, before it goes into the void.
162 // make sure that views using this buffer
167 if (!tmppath.empty()) {
168 DestroyBufferTmpDir(tmppath);
171 Paragraph * par = paragraph;
174 tmppar = par->next();
182 string const Buffer::getLatexName(bool no_path) const
184 string name = ChangeExtension(MakeLatexName(filename), ".tex");
186 return OnlyFilename(name);
192 pair<Buffer::LogType, string> const Buffer::getLogName(void) const
194 string const filename = getLatexName(false);
196 if (filename.empty())
197 return make_pair(Buffer::latexlog, string());
199 string path = OnlyPath(filename);
201 if (lyxrc.use_tempdir || (IsDirWriteable(path) < 1))
204 string const fname = AddName(path,
205 OnlyFilename(ChangeExtension(filename,
208 AddName(path, OnlyFilename(
209 ChangeExtension(filename,
210 formats.Extension("literate") + ".out")));
212 // If no Latex log or Build log is newer, show Build log
214 FileInfo const f_fi(fname);
215 FileInfo const b_fi(bname);
218 (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
219 lyxerr[Debug::FILES] << "Log name calculated as : " << bname << endl;
220 return make_pair(Buffer::buildlog, bname);
222 lyxerr[Debug::FILES] << "Log name calculated as : " << fname << endl;
223 return make_pair(Buffer::latexlog, fname);
227 void Buffer::setReadonly(bool flag)
229 if (read_only != flag) {
232 users->owner()->getDialogs()->updateBufferDependent(false);
235 WarnReadonly(filename);
240 bool Buffer::saveParamsAsDefaults() // const
242 string const fname = AddName(AddPath(user_lyxdir, "templates/"),
244 Buffer defaults = Buffer(fname);
246 // Use the current buffer's parameters as default
247 defaults.params = params;
249 // add an empty paragraph. Is this enough?
250 defaults.paragraph = new Paragraph;
252 return defaults.writeFile(defaults.filename, false);
256 /// Update window titles of all users
257 // Should work on a list
258 void Buffer::updateTitles() const
260 if (users) users->owner()->updateWindowTitle();
264 /// Reset autosave timer of all users
265 // Should work on a list
266 void Buffer::resetAutosaveTimers() const
268 if (users) users->owner()->resetAutosaveTimer();
272 void Buffer::setFileName(string const & newfile)
274 filename = MakeAbsPath(newfile);
275 filepath = OnlyPath(filename);
276 setReadonly(IsFileWriteable(filename) == 0);
281 // We'll remove this later. (Lgb)
284 string last_inset_read;
288 ErtComp() : active(false), in_tabular(false) {
295 std::stack<ErtComp> ert_stack;
303 // candidate for move to BufferView
304 // (at least some parts in the beginning of the func)
307 // changed to be public and have one parameter
308 // if par = 0 normal behavior
309 // else insert behavior
310 // Returns false if "\the_end" is not read for formats >= 2.13. (Asger)
311 bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
314 ert_comp.contents.erase();
315 ert_comp.active = false;
316 ert_comp.in_tabular = false;
320 Paragraph::depth_type depth = 0;
321 bool the_end_read = false;
323 Paragraph * first_par = 0;
324 LyXFont font(LyXFont::ALL_INHERIT, params.language);
325 if (file_format < 216 && params.language->lang() == "hebrew")
326 font.setLanguage(default_language);
331 // We are inserting into an existing document
332 users->text->breakParagraph(users);
333 first_par = users->text->firstParagraph();
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 paragraph = first_par;
372 void Buffer::insertErtContents(Paragraph * par, int & pos,
373 LyXFont const & font, bool set_inactive)
375 if (!ert_comp.contents.empty()) {
376 lyxerr[Debug::INSETS] << "ERT contents:\n"
377 << ert_comp.contents << endl;
378 Inset * inset = new InsetERT(ert_comp.contents);
379 par->insertInset(pos++, inset, font);
380 ert_comp.contents.erase();
383 ert_comp.active = false;
389 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
390 Paragraph *& first_par,
391 string const & token, int & pos,
392 Paragraph::depth_type & depth,
396 bool the_end_read = false;
397 #ifndef NO_PEXTRA_REALLY
398 // This is super temporary but is needed to get the compability
399 // mode for minipages work correctly together with new tabulars.
400 static int call_depth;
402 bool checkminipage = false;
403 static Paragraph * minipar;
404 static Paragraph * parBeforeMinipage;
407 if (token[0] != '\\') {
409 if (ert_comp.active) {
410 ert_comp.contents += token;
413 for (string::const_iterator cit = token.begin();
414 cit != token.end(); ++cit) {
415 par->insertChar(pos, (*cit), font);
418 checkminipage = true;
422 } else if (token == "\\i") {
423 Inset * inset = new InsetLatexAccent;
424 inset->read(this, lex);
425 par->insertInset(pos, inset, font);
427 } else if (token == "\\layout") {
429 ert_comp.in_tabular = false;
431 insertErtContents(par, pos, font);
434 string const layoutname = lex.GetString();
435 pair<bool, LyXTextClass::LayoutList::size_type> pp
436 = textclasslist.NumberOfLayout(params.textclass,
440 if (compare_no_case(layoutname, "latex") == 0) {
441 ert_comp.active = true;
445 // The is the compability reading of layout caption.
446 // It can be removed in LyX version 1.3.0. (Lgb)
447 if (compare_no_case(layoutname, "caption") == 0) {
448 // We expect that the par we are now working on is
449 // really inside a InsetText inside a InsetFloat.
450 // We also know that captions can only be
451 // one paragraph. (Lgb)
453 // We should now read until the next "\layout"
455 // This is probably not good enough, what if the
456 // caption is the last par in the document (Lgb)
457 istream & ist = lex.getStream();
463 if (prefixIs(line, "\\layout")) {
467 if (prefixIs(line, "\\begin_inset"))
469 if (prefixIs(line, "\\end_inset")) {
480 // Now we should have the whole layout in ss
481 // we should now be able to give this to the
483 ss << "\\end_inset\n";
485 // This seems like a bug in stringstream.
486 // We really should be able to use ss
488 istringstream is(ss.str());
490 tmplex.setStream(is);
491 Inset * inset = new InsetCaption;
492 inset->Read(this, tmplex);
493 par->InsertInset(pos, inset, font);
500 par = new Paragraph(par);
504 par->layout = pp.second;
507 // use default layout "Standard" (0)
510 // Test whether the layout is obsolete.
511 LyXLayout const & layout =
512 textclasslist.Style(params.textclass,
514 if (!layout.obsoleted_by().empty())
515 par->layout = textclasslist
516 .NumberOfLayout(params.textclass,
517 layout.obsoleted_by())
519 par->params().depth(depth);
520 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
521 if (file_format < 216
522 && params.language->lang() == "hebrew")
523 font.setLanguage(default_language);
528 } else if (token == "\\begin_float") {
529 // This is the compability reader. It can be removed in
530 // LyX version 1.3.0. (Lgb)
532 string const tmptok = lex.GetString();
533 //lyxerr << "old float: " << tmptok << endl;
536 stringstream old_float;
538 if (tmptok == "footnote") {
539 inset = new InsetFoot;
540 } else if (tmptok == "margin") {
541 inset = new InsetMarginal;
542 } else if (tmptok == "fig") {
543 inset = new InsetFloat("figure");
544 old_float << "placement htbp\n"
546 } else if (tmptok == "tab") {
547 inset = new InsetFloat("table");
548 old_float << "placement htbp\n"
550 } else if (tmptok == "alg") {
551 inset = new InsetFloat("algorithm");
552 old_float << "placement htbp\n"
554 } else if (tmptok == "wide-fig") {
555 inset = new InsetFloat("figure");
556 //InsetFloat * tmp = new InsetFloat("figure");
559 old_float << "placement htbp\n"
561 } else if (tmptok == "wide-tab") {
562 inset = new InsetFloat("table");
563 //InsetFloat * tmp = new InsetFloat("table");
566 old_float << "placement htbp\n"
572 return false; // no end read yet
575 old_float << "collapsed true\n";
577 // Here we need to check for \end_deeper and handle that
578 // before we do the footnote parsing.
579 // This _is_ a hack! (Lgb)
582 string const tmp = lex.GetString();
583 if (tmp == "\\end_deeper") {
584 //lyxerr << "\\end_deeper caught!" << endl;
586 lex.printError("\\end_deeper: "
587 "depth is already null");
592 old_float << tmp << ' ';
597 old_float << lex.getLongString("\\end_float")
598 << "\n\\end_inset\n";
599 //lyxerr << "Float Body:\n" << old_float.str() << endl;
600 // That this does not work seems like a bug
601 // in stringstream. (Lgb)
602 istringstream istr(old_float.str());
604 nylex.setStream(istr);
605 inset->read(this, nylex);
606 par->insertInset(pos, inset, font);
608 } else if (token == "\\begin_deeper") {
610 } else if (token == "\\end_deeper") {
612 lex.printError("\\end_deeper: "
613 "depth is already null");
617 } else if (token == "\\begin_preamble") {
618 params.readPreamble(lex);
619 } else if (token == "\\textclass") {
621 pair<bool, LyXTextClassList::size_type> pp =
622 textclasslist.NumberOfClass(lex.GetString());
624 params.textclass = pp.second;
626 WriteAlert(string(_("Textclass error")),
627 string(_("The document uses an unknown textclass \"")) +
628 lex.GetString() + string("\"."),
629 string(_("LyX will not be able to produce output correctly.")));
630 params.textclass = 0;
632 if (!textclasslist.Load(params.textclass)) {
633 // if the textclass wasn't loaded properly
634 // we need to either substitute another
635 // or stop loading the file.
636 // I can substitute but I don't see how I can
637 // stop loading... ideas?? ARRae980418
638 WriteAlert(_("Textclass Loading Error!"),
639 string(_("Can't load textclass ")) +
640 textclasslist.NameOfClass(params.textclass),
641 _("-- substituting default"));
642 params.textclass = 0;
644 } else if (token == "\\options") {
646 params.options = lex.GetString();
647 } else if (token == "\\language") {
648 params.readLanguage(lex);
649 } else if (token == "\\fontencoding") {
651 } else if (token == "\\inputencoding") {
653 params.inputenc = lex.GetString();
654 } else if (token == "\\graphics") {
655 params.readGraphicsDriver(lex);
656 } else if (token == "\\fontscheme") {
658 params.fonts = lex.GetString();
659 } else if (token == "\\noindent") {
660 par->params().noindent(true);
661 } else if (token == "\\fill_top") {
662 par->params().spaceTop(VSpace(VSpace::VFILL));
663 } else if (token == "\\fill_bottom") {
664 par->params().spaceBottom(VSpace(VSpace::VFILL));
665 } else if (token == "\\line_top") {
666 par->params().lineTop(true);
667 } else if (token == "\\line_bottom") {
668 par->params().lineBottom(true);
669 } else if (token == "\\pagebreak_top") {
670 par->params().pagebreakTop(true);
671 } else if (token == "\\pagebreak_bottom") {
672 par->params().pagebreakBottom(true);
673 } else if (token == "\\start_of_appendix") {
674 par->params().startOfAppendix(true);
675 } else if (token == "\\paragraph_separation") {
676 int tmpret = lex.FindToken(string_paragraph_separation);
677 if (tmpret == -1) ++tmpret;
678 if (tmpret != LYX_LAYOUT_DEFAULT)
679 params.paragraph_separation =
680 static_cast<BufferParams::PARSEP>(tmpret);
681 } else if (token == "\\defskip") {
683 params.defskip = VSpace(lex.GetString());
684 } else if (token == "\\epsfig") { // obsolete
685 // Indeed it is obsolete, but we HAVE to be backwards
686 // compatible until 0.14, because otherwise all figures
687 // in existing documents are irretrivably lost. (Asger)
688 params.readGraphicsDriver(lex);
689 } else if (token == "\\quotes_language") {
690 int tmpret = lex.FindToken(string_quotes_language);
691 if (tmpret == -1) ++tmpret;
692 if (tmpret != LYX_LAYOUT_DEFAULT) {
693 InsetQuotes::quote_language tmpl =
694 InsetQuotes::EnglishQ;
697 tmpl = InsetQuotes::EnglishQ;
700 tmpl = InsetQuotes::SwedishQ;
703 tmpl = InsetQuotes::GermanQ;
706 tmpl = InsetQuotes::PolishQ;
709 tmpl = InsetQuotes::FrenchQ;
712 tmpl = InsetQuotes::DanishQ;
715 params.quotes_language = tmpl;
717 } else if (token == "\\quotes_times") {
719 switch (lex.GetInteger()) {
721 params.quotes_times = InsetQuotes::SingleQ;
724 params.quotes_times = InsetQuotes::DoubleQ;
727 } else if (token == "\\papersize") {
728 int tmpret = lex.FindToken(string_papersize);
732 params.papersize2 = tmpret;
733 } else if (token == "\\paperpackage") {
734 int tmpret = lex.FindToken(string_paperpackages);
737 params.paperpackage = BufferParams::PACKAGE_NONE;
739 params.paperpackage = tmpret;
740 } else if (token == "\\use_geometry") {
742 params.use_geometry = lex.GetInteger();
743 } else if (token == "\\use_amsmath") {
745 params.use_amsmath = lex.GetInteger();
746 } else if (token == "\\paperorientation") {
747 int tmpret = lex.FindToken(string_orientation);
748 if (tmpret == -1) ++tmpret;
749 if (tmpret != LYX_LAYOUT_DEFAULT)
750 params.orientation = static_cast<BufferParams::PAPER_ORIENTATION>(tmpret);
751 } else if (token == "\\paperwidth") {
753 params.paperwidth = lex.GetString();
754 } else if (token == "\\paperheight") {
756 params.paperheight = lex.GetString();
757 } else if (token == "\\leftmargin") {
759 params.leftmargin = lex.GetString();
760 } else if (token == "\\topmargin") {
762 params.topmargin = lex.GetString();
763 } else if (token == "\\rightmargin") {
765 params.rightmargin = lex.GetString();
766 } else if (token == "\\bottommargin") {
768 params.bottommargin = lex.GetString();
769 } else if (token == "\\headheight") {
771 params.headheight = lex.GetString();
772 } else if (token == "\\headsep") {
774 params.headsep = lex.GetString();
775 } else if (token == "\\footskip") {
777 params.footskip = lex.GetString();
778 } else if (token == "\\paperfontsize") {
780 params.fontsize = strip(lex.GetString());
781 } else if (token == "\\papercolumns") {
783 params.columns = lex.GetInteger();
784 } else if (token == "\\papersides") {
786 switch (lex.GetInteger()) {
788 case 1: params.sides = LyXTextClass::OneSide; break;
789 case 2: params.sides = LyXTextClass::TwoSides; break;
791 } else if (token == "\\paperpagestyle") {
793 params.pagestyle = strip(lex.GetString());
794 } else if (token == "\\bullet") {
796 int const index = lex.GetInteger();
798 int temp_int = lex.GetInteger();
799 params.user_defined_bullets[index].setFont(temp_int);
800 params.temp_bullets[index].setFont(temp_int);
802 temp_int = lex.GetInteger();
803 params.user_defined_bullets[index].setCharacter(temp_int);
804 params.temp_bullets[index].setCharacter(temp_int);
806 temp_int = lex.GetInteger();
807 params.user_defined_bullets[index].setSize(temp_int);
808 params.temp_bullets[index].setSize(temp_int);
810 string const temp_str = lex.GetString();
811 if (temp_str != "\\end_bullet") {
812 // this element isn't really necessary for
813 // parsing but is easier for humans
814 // to understand bullets. Put it back and
815 // set a debug message?
816 lex.printError("\\end_bullet expected, got" + temp_str);
817 //how can I put it back?
819 } else if (token == "\\bulletLaTeX") {
821 int const index = lex.GetInteger();
823 string temp_str = lex.GetString();
825 while (temp_str != "\\end_bullet") {
826 // this loop structure is needed when user
827 // enters an empty string since the first
828 // thing returned will be the \\end_bullet
830 // if the LaTeX entry has spaces. Each element
831 // therefore needs to be read in turn
834 temp_str = lex.GetString();
836 params.user_defined_bullets[index].setText(sum_str);
837 params.temp_bullets[index].setText(sum_str);
838 } else if (token == "\\secnumdepth") {
840 params.secnumdepth = lex.GetInteger();
841 } else if (token == "\\tocdepth") {
843 params.tocdepth = lex.GetInteger();
844 } else if (token == "\\spacing") {
846 string const tmp = strip(lex.GetString());
847 Spacing::Space tmp_space = Spacing::Default;
849 if (tmp == "single") {
850 tmp_space = Spacing::Single;
851 } else if (tmp == "onehalf") {
852 tmp_space = Spacing::Onehalf;
853 } else if (tmp == "double") {
854 tmp_space = Spacing::Double;
855 } else if (tmp == "other") {
857 tmp_space = Spacing::Other;
858 tmp_val = lex.GetFloat();
860 lex.printError("Unknown spacing token: '$$Token'");
862 // Small hack so that files written with klyx will be
865 par->params().spacing(Spacing(tmp_space, tmp_val));
867 params.spacing.set(tmp_space, tmp_val);
869 } else if (token == "\\paragraph_spacing") {
871 string const tmp = strip(lex.GetString());
872 if (tmp == "single") {
873 par->params().spacing(Spacing(Spacing::Single));
874 } else if (tmp == "onehalf") {
875 par->params().spacing(Spacing(Spacing::Onehalf));
876 } else if (tmp == "double") {
877 par->params().spacing(Spacing(Spacing::Double));
878 } else if (tmp == "other") {
880 par->params().spacing(Spacing(Spacing::Other,
883 lex.printError("Unknown spacing token: '$$Token'");
885 } else if (token == "\\float_placement") {
887 params.float_placement = lex.GetString();
888 } else if (token == "\\family") {
890 font.setLyXFamily(lex.GetString());
891 } else if (token == "\\series") {
893 font.setLyXSeries(lex.GetString());
894 } else if (token == "\\shape") {
896 font.setLyXShape(lex.GetString());
897 } else if (token == "\\size") {
899 font.setLyXSize(lex.GetString());
902 #warning compatability hack needed
904 } else if (token == "\\latex") {
906 string const tok = lex.GetString();
907 // This is dirty, but gone with LyX3. (Asger)
908 if (tok == "no_latex")
909 font.setLatex(LyXFont::OFF);
910 else if (tok == "latex")
911 font.setLatex(LyXFont::ON);
912 else if (tok == "default")
913 font.setLatex(LyXFont::INHERIT);
915 lex.printError("Unknown LaTeX font flag "
918 } else if (token == "\\latex") {
920 string const tok = lex.GetString();
921 if (tok == "no_latex") {
923 insertErtContents(par, pos, font);
924 } else if (tok == "latex") {
925 ert_comp.active = true;
926 } else if (tok == "default") {
928 insertErtContents(par, pos, font);
930 lex.printError("Unknown LaTeX font flag "
934 } else if (token == "\\lang") {
936 string const tok = lex.GetString();
937 Language const * lang = languages.getLanguage(tok);
939 font.setLanguage(lang);
941 font.setLanguage(params.language);
942 lex.printError("Unknown language `$$Token'");
944 } else if (token == "\\numeric") {
946 font.setNumber(font.setLyXMisc(lex.GetString()));
947 } else if (token == "\\emph") {
949 font.setEmph(font.setLyXMisc(lex.GetString()));
950 } else if (token == "\\bar") {
952 string const tok = lex.GetString();
953 // This is dirty, but gone with LyX3. (Asger)
955 font.setUnderbar(LyXFont::ON);
956 else if (tok == "no")
957 font.setUnderbar(LyXFont::OFF);
958 else if (tok == "default")
959 font.setUnderbar(LyXFont::INHERIT);
961 lex.printError("Unknown bar font flag "
963 } else if (token == "\\noun") {
965 font.setNoun(font.setLyXMisc(lex.GetString()));
966 } else if (token == "\\color") {
968 font.setLyXColor(lex.GetString());
969 } else if (token == "\\align") {
970 int tmpret = lex.FindToken(string_align);
971 if (tmpret == -1) ++tmpret;
972 if (tmpret != LYX_LAYOUT_DEFAULT) { // tmpret != 99 ???
973 int const tmpret2 = int(pow(2.0, tmpret));
974 //lyxerr << "Tmpret2 = " << tmpret2 << endl;
975 par->params().align(LyXAlignment(tmpret2));
977 } else if (token == "\\added_space_top") {
979 par->params().spaceTop(VSpace(lex.GetString()));
980 } else if (token == "\\added_space_bottom") {
982 par->params().spaceBottom(VSpace(lex.GetString()));
983 #ifndef NO_PEXTRA_REALLY
984 } else if (token == "\\pextra_type") {
986 par->params().pextraType(lex.GetInteger());
987 } else if (token == "\\pextra_width") {
989 par->params().pextraWidth(lex.GetString());
990 } else if (token == "\\pextra_widthp") {
992 par->params().pextraWidthp(lex.GetString());
993 } else if (token == "\\pextra_alignment") {
995 par->params().pextraAlignment(lex.GetInteger());
996 } else if (token == "\\pextra_hfill") {
998 par->params().pextraHfill(lex.GetInteger());
999 } else if (token == "\\pextra_start_minipage") {
1001 par->params().pextraStartMinipage(lex.GetInteger());
1003 } else if (token == "\\labelwidthstring") {
1005 par->params().labelWidthString(lex.GetString());
1006 // do not delete this token, it is still needed!
1007 } else if (token == "\\end_inset") {
1008 lyxerr << "Solitary \\end_inset. Missing \\begin_inset?.\n"
1009 << "Last inset read was: " << last_inset_read
1011 // Simply ignore this. The insets do not have
1013 // But insets should read it, it is a part of
1014 // the inset isn't it? Lgb.
1015 } else if (token == "\\begin_inset") {
1017 insertErtContents(par, pos, font, false);
1018 ert_stack.push(ert_comp);
1019 ert_comp = ErtComp();
1021 readInset(lex, par, pos, font);
1023 ert_comp = ert_stack.top();
1026 } else if (token == "\\SpecialChar") {
1027 LyXLayout const & layout =
1028 textclasslist.Style(params.textclass,
1031 // Insets don't make sense in a free-spacing context! ---Kayvan
1032 if (layout.free_spacing) {
1035 string next_token = lex.GetString();
1036 if (next_token == "\\-") {
1037 par->insertChar(pos, '-', font);
1038 } else if (next_token == "\\protected_separator"
1039 || next_token == "~") {
1040 par->insertChar(pos, ' ', font);
1042 lex.printError("Token `$$Token' "
1044 "paragraph layout!");
1049 Inset * inset = new InsetSpecialChar;
1050 inset->read(this, lex);
1051 par->insertInset(pos, inset, font);
1054 } else if (token == "\\newline") {
1057 if (!ert_comp.in_tabular && ert_comp.active) {
1058 ert_comp.contents += char(Paragraph::META_NEWLINE);
1060 // Since we cannot know it this is only a regular
1061 // newline or a tabular cell delimter we have to
1062 // handle the ERT here.
1063 insertErtContents(par, pos, font, false);
1065 par->insertChar(pos, Paragraph::META_NEWLINE, font);
1069 par->insertChar(pos, Paragraph::META_NEWLINE, font);
1072 } else if (token == "\\LyXTable") {
1074 ert_comp.in_tabular = true;
1076 Inset * inset = new InsetTabular(*this);
1077 inset->read(this, lex);
1078 par->insertInset(pos, inset, font);
1080 // because of OLD_TABULAR_READ where tabulars have been
1082 checkminipage = true;
1083 } else if (token == "\\hfill") {
1084 par->insertChar(pos, Paragraph::META_HFILL, font);
1086 } else if (token == "\\protected_separator") { // obsolete
1087 // This is a backward compability thingie. (Lgb)
1088 // Remove it later some time...introduced with fileformat
1090 LyXLayout const & layout =
1091 textclasslist.Style(params.textclass,
1094 if (layout.free_spacing) {
1095 par->insertChar(pos, ' ', font);
1097 Inset * inset = new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
1098 par->insertInset(pos, inset, font);
1101 } else if (token == "\\bibitem") { // ale970302
1103 InsetCommandParams p("bibitem", "dummy");
1104 par->bibkey = new InsetBibKey(p);
1106 par->bibkey->read(this, lex);
1107 } else if (token == "\\backslash") {
1109 if (ert_comp.active) {
1110 ert_comp.contents += "\\";
1113 par->insertChar(pos, '\\', font);
1118 } else if (token == "\\the_end") {
1120 // If we still have some ert active here we have to insert
1121 // it so we don't loose it. (Lgb)
1122 insertErtContents(par, pos, font);
1124 the_end_read = true;
1125 minipar = parBeforeMinipage = 0;
1128 if (ert_comp.active) {
1129 ert_comp.contents += token;
1132 // This should be insurance for the future: (Asger)
1133 lex.printError("Unknown token `$$Token'. "
1134 "Inserting as text.");
1135 string::const_iterator cit = token.begin();
1136 string::const_iterator end = token.end();
1137 for (; cit != end; ++cit) {
1138 par->insertChar(pos, (*cit), font);
1145 #ifndef NO_PEXTRA_REALLY
1146 // now check if we have a minipage paragraph as at this
1147 // point we already read all the necessary data!
1148 // this cannot be done in layout because there we did
1149 // not read yet the paragraph PEXTRA-params (Jug)
1151 // BEGIN pextra_minipage compability
1152 // This should be removed in 1.3.x (Lgb)
1154 // This compability code is not perfect. In a couple
1155 // of rand cases it fails. When the minipage par is
1156 // the first par in the document, and when there are
1157 // none or only one regular paragraphs after the
1158 // minipage. Currently I am not investing any effort
1159 // in fixing those cases.
1161 //lyxerr << "Call depth: " << call_depth << endl;
1162 if (checkminipage && (call_depth == 1)) {
1163 checkminipage = false;
1164 if (minipar && (minipar != par) &&
1165 (par->params().pextraType()==Paragraph::PEXTRA_MINIPAGE))
1167 lyxerr << "minipages in a row" << endl;
1168 if (par->params().pextraStartMinipage()) {
1169 lyxerr << "start new minipage" << endl;
1170 // minipages in a row
1171 par->previous()->next(0);
1174 Paragraph * tmp = minipar;
1176 tmp->params().pextraType(0);
1177 tmp->params().pextraWidth(string());
1178 tmp->params().pextraWidthp(string());
1179 tmp->params().pextraAlignment(0);
1180 tmp->params().pextraHfill(false);
1181 tmp->params().pextraStartMinipage(false);
1184 // create a new paragraph to insert the
1185 // minipages in the following case
1186 if (par->params().pextraStartMinipage() &&
1187 !par->params().pextraHfill())
1189 Paragraph * p = new Paragraph;
1191 p->previous(parBeforeMinipage);
1192 parBeforeMinipage->next(p);
1194 p->params().depth(parBeforeMinipage->params().depth());
1195 parBeforeMinipage = p;
1197 InsetMinipage * mini = new InsetMinipage;
1198 mini->pos(static_cast<InsetMinipage::Position>(par->params().pextraAlignment()));
1199 mini->width(par->params().pextraWidth());
1200 if (!par->params().pextraWidthp().empty()) {
1201 lyxerr << "WP:" << mini->width() << endl;
1202 mini->width(tostr(par->params().pextraWidthp())+"%");
1204 mini->inset.paragraph(par);
1205 // Insert the minipage last in the
1206 // previous paragraph.
1207 if (par->params().pextraHfill()) {
1208 parBeforeMinipage->insertChar
1209 (parBeforeMinipage->size(), Paragraph::META_HFILL);
1211 parBeforeMinipage->insertInset
1212 (parBeforeMinipage->size(), mini);
1216 lyxerr << "new minipage par" << endl;
1217 //nothing to do just continue reading
1220 } else if (minipar && (minipar != par)) {
1221 lyxerr << "last minipage par read" << endl;
1222 // The last paragraph read was not part of a
1223 // minipage but the par linked list is...
1224 // So we need to remove the last par from the
1226 if (par->previous())
1227 par->previous()->next(0);
1228 par->previous(parBeforeMinipage);
1229 parBeforeMinipage->next(par);
1230 Paragraph * tmp = minipar;
1232 tmp->params().pextraType(0);
1233 tmp->params().pextraWidth(string());
1234 tmp->params().pextraWidthp(string());
1235 tmp->params().pextraAlignment(0);
1236 tmp->params().pextraHfill(false);
1237 tmp->params().pextraStartMinipage(false);
1240 depth = parBeforeMinipage->params().depth();
1241 minipar = parBeforeMinipage = 0;
1242 } else if (!minipar &&
1243 (par->params().pextraType() == Paragraph::PEXTRA_MINIPAGE))
1245 // par is the first paragraph in a minipage
1246 lyxerr << "begin minipage" << endl;
1247 // To minimize problems for
1248 // the users we will insert
1249 // the first minipage in
1250 // a sequence of minipages
1251 // in its own paragraph.
1252 Paragraph * p = new Paragraph;
1254 p->previous(par->previous());
1256 p->params().depth(depth);
1257 par->params().depth(0);
1259 if (par->previous())
1260 par->previous()->next(p);
1262 parBeforeMinipage = p;
1264 if (!first_par || (first_par == par))
1267 InsetMinipage * mini = new InsetMinipage;
1268 mini->pos(static_cast<InsetMinipage::Position>(minipar->params().pextraAlignment()));
1269 mini->width(minipar->params().pextraWidth());
1270 if (!par->params().pextraWidthp().empty()) {
1271 lyxerr << "WP:" << mini->width() << endl;
1272 mini->width(tostr(par->params().pextraWidthp())+"%");
1274 mini->inset.paragraph(minipar);
1276 // Insert the minipage last in the
1277 // previous paragraph.
1278 if (minipar->params().pextraHfill()) {
1279 parBeforeMinipage->insertChar
1280 (parBeforeMinipage->size(),Paragraph::META_HFILL);
1282 parBeforeMinipage->insertInset
1283 (parBeforeMinipage->size(), mini);
1286 // End of pextra_minipage compability
1289 return the_end_read;
1293 void Buffer::readInset(LyXLex & lex, Paragraph *& par,
1294 int & pos, LyXFont & font)
1296 // consistency check
1297 if (lex.GetString() != "\\begin_inset") {
1298 lyxerr << "Buffer::readInset: Consistency check failed."
1305 string const tmptok = lex.GetString();
1306 last_inset_read = tmptok;
1308 // test the different insets
1309 if (tmptok == "LatexCommand") {
1310 InsetCommandParams inscmd;
1313 string const cmdName = inscmd.getCmdName();
1315 if (cmdName == "cite") {
1316 inset = new InsetCitation(inscmd);
1317 } else if (cmdName == "bibitem") {
1318 lex.printError("Wrong place for bibitem");
1319 inset = new InsetBibKey(inscmd);
1320 } else if (cmdName == "BibTeX") {
1321 inset = new InsetBibtex(inscmd);
1322 } else if (cmdName == "index") {
1323 inset = new InsetIndex(inscmd);
1324 } else if (cmdName == "include") {
1325 inset = new InsetInclude(inscmd, *this);
1326 } else if (cmdName == "label") {
1327 inset = new InsetLabel(inscmd);
1328 } else if (cmdName == "url"
1329 || cmdName == "htmlurl") {
1330 inset = new InsetUrl(inscmd);
1331 } else if (cmdName == "ref"
1332 || cmdName == "pageref"
1333 || cmdName == "vref"
1334 || cmdName == "vpageref"
1335 || cmdName == "prettyref") {
1336 if (!inscmd.getOptions().empty()
1337 || !inscmd.getContents().empty()) {
1338 inset = new InsetRef(inscmd, *this);
1340 } else if (cmdName == "tableofcontents") {
1341 inset = new InsetTOC(inscmd);
1342 } else if (cmdName == "listofalgorithms") {
1343 inset = new InsetFloatList("algorithm");
1344 } else if (cmdName == "listoffigures") {
1345 inset = new InsetFloatList("figure");
1346 } else if (cmdName == "listoftables") {
1347 inset = new InsetFloatList("table");
1348 } else if (cmdName == "printindex") {
1349 inset = new InsetPrintIndex(inscmd);
1350 } else if (cmdName == "lyxparent") {
1351 inset = new InsetParent(inscmd, *this);
1354 if (tmptok == "Quotes") {
1355 inset = new InsetQuotes;
1356 } else if (tmptok == "External") {
1357 inset = new InsetExternal;
1358 } else if (tmptok == "FormulaMacro") {
1359 inset = new InsetFormulaMacro;
1360 } else if (tmptok == "Formula") {
1361 inset = new InsetFormula;
1362 } else if (tmptok == "Figure") {
1363 inset = new InsetFig(100, 100, *this);
1364 } else if (tmptok == "Info") {
1365 inset = new InsetInfo;
1366 } else if (tmptok == "Include") {
1367 InsetCommandParams p( "Include" );
1368 inset = new InsetInclude(p, *this);
1369 } else if (tmptok == "ERT") {
1370 inset = new InsetERT;
1371 } else if (tmptok == "Tabular") {
1372 inset = new InsetTabular(*this);
1373 } else if (tmptok == "Text") {
1374 inset = new InsetText;
1375 } else if (tmptok == "Foot") {
1376 inset = new InsetFoot;
1377 } else if (tmptok == "Marginal") {
1378 inset = new InsetMarginal;
1379 } else if (tmptok == "Minipage") {
1380 inset = new InsetMinipage;
1381 } else if (tmptok == "Float") {
1383 string tmptok = lex.GetString();
1384 inset = new InsetFloat(tmptok);
1385 } else if (tmptok == "List") {
1386 inset = new InsetList;
1387 } else if (tmptok == "Theorem") {
1388 inset = new InsetList;
1389 } else if (tmptok == "Caption") {
1390 inset = new InsetCaption;
1391 } else if (tmptok == "GRAPHICS") {
1392 inset = new InsetGraphics;
1393 } else if (tmptok == "FloatList") {
1394 inset = new InsetFloatList;
1397 if (inset) inset->read(this, lex);
1401 par->insertInset(pos, inset, font);
1407 bool Buffer::readFile(LyXLex & lex, Paragraph * par)
1411 string const token(lex.GetString());
1412 if (token == "\\lyxformat") { // the first token _must_ be...
1414 string tmp_format = lex.GetString();
1415 //lyxerr << "LyX Format: `" << tmp_format << "'" << endl;
1416 // if present remove ".," from string.
1417 string::size_type dot = tmp_format.find_first_of(".,");
1418 //lyxerr << " dot found at " << dot << endl;
1419 if (dot != string::npos)
1420 tmp_format.erase(dot, 1);
1421 file_format = strToInt(tmp_format);
1422 if (file_format == LYX_FORMAT) {
1424 } else if (file_format > LYX_FORMAT) {
1426 WriteAlert(_("Warning!"),
1427 _("LyX file format is newer that what"),
1428 _("is supported in this LyX version. Expect some problems."));
1430 } else if (file_format < LYX_FORMAT) {
1432 if (file_format < 200) {
1433 WriteAlert(_("ERROR!"),
1434 _("Old LyX file format found. "
1435 "Use LyX 0.10.x to read this!"));
1439 bool the_end = readLyXformat2(lex, par);
1441 // the_end was added in 213
1442 if (file_format < 213)
1446 WriteAlert(_("Warning!"),
1447 _("Reading of document is not complete"),
1448 _("Maybe the document is truncated"));
1450 } else { // "\\lyxformat" not found
1451 WriteAlert(_("ERROR!"), _("Not a LyX file!"));
1454 WriteAlert(_("ERROR!"), _("Unable to read file!"));
1459 // Should probably be moved to somewhere else: BufferView? LyXView?
1460 bool Buffer::save() const
1462 // We don't need autosaves in the immediate future. (Asger)
1463 resetAutosaveTimers();
1467 if (lyxrc.make_backup) {
1468 s = fileName() + '~';
1469 if (!lyxrc.backupdir_path.empty())
1470 s = AddName(lyxrc.backupdir_path,
1471 subst(os::slashify_path(s),'/','!'));
1473 // Rename is the wrong way of making a backup,
1474 // this is the correct way.
1475 /* truss cp fil fil2:
1476 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
1477 stat("LyXVC.lyx", 0xEFFFF688) = 0
1478 open("LyXVC.lyx", O_RDONLY) = 3
1479 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
1480 fstat(4, 0xEFFFF508) = 0
1481 fstat(3, 0xEFFFF508) = 0
1482 read(3, " # T h i s f i l e w".., 8192) = 5579
1483 write(4, " # T h i s f i l e w".., 5579) = 5579
1484 read(3, 0xEFFFD4A0, 8192) = 0
1487 chmod("LyXVC3.lyx", 0100644) = 0
1488 lseek(0, 0, SEEK_CUR) = 46440
1492 // Should probably have some more error checking here.
1493 // Should be cleaned up in 0.13, at least a bit.
1494 // Doing it this way, also makes the inodes stay the same.
1495 // This is still not a very good solution, in particular we
1496 // might loose the owner of the backup.
1497 FileInfo finfo(fileName());
1498 if (finfo.exist()) {
1499 mode_t fmode = finfo.getMode();
1500 struct utimbuf times = {
1501 finfo.getAccessTime(),
1502 finfo.getModificationTime() };
1504 ifstream ifs(fileName().c_str());
1505 ofstream ofs(s.c_str(), ios::out|ios::trunc);
1510 ::chmod(s.c_str(), fmode);
1512 if (::utime(s.c_str(), ×)) {
1513 lyxerr << "utime error." << endl;
1516 lyxerr << "LyX was not able to make "
1517 "backup copy. Beware." << endl;
1522 if (writeFile(fileName(), false)) {
1524 removeAutosaveFile(fileName());
1526 // Saving failed, so backup is not backup
1527 if (lyxrc.make_backup) {
1528 lyx::rename(s, fileName());
1536 // Returns false if unsuccesful
1537 bool Buffer::writeFile(string const & fname, bool flag) const
1539 // if flag is false writeFile will not create any GUI
1540 // warnings, only cerr.
1541 // Needed for autosave in background or panic save (Matthias 120496)
1543 if (read_only && (fname == filename)) {
1544 // Here we should come with a question if we should
1545 // perform the write anyway.
1547 lyxerr << _("Error! Document is read-only: ")
1550 WriteAlert(_("Error! Document is read-only: "),
1555 FileInfo finfo(fname);
1556 if (finfo.exist() && !finfo.writable()) {
1557 // Here we should come with a question if we should
1558 // try to do the save anyway. (i.e. do a chmod first)
1560 lyxerr << _("Error! Cannot write file: ")
1563 WriteFSAlert(_("Error! Cannot write file: "),
1568 ofstream ofs(fname.c_str());
1571 lyxerr << _("Error! Cannot open file: ")
1574 WriteFSAlert(_("Error! Cannot open file: "),
1580 // Use the standard "C" locale for file output.
1581 ofs.imbue(std::locale::classic());
1584 // The top of the file should not be written by params.
1586 // write out a comment in the top of the file
1587 ofs << '#' << LYX_DOCVERSION
1588 << " created this file. For more info see http://www.lyx.org/\n"
1589 << "\\lyxformat " << LYX_FORMAT << "\n";
1591 // now write out the buffer paramters.
1592 params.writeFile(ofs);
1594 Paragraph::depth_type depth = 0;
1596 // this will write out all the paragraphs
1597 // using recursive descent.
1598 paragraph->writeFile(this, ofs, params, depth);
1600 // Write marker that shows file is complete
1601 ofs << "\n\\the_end" << endl;
1605 // how to check if close went ok?
1606 // Following is an attempt... (BE 20001011)
1608 // good() returns false if any error occured, including some
1609 // formatting error.
1610 // bad() returns true if something bad happened in the buffer,
1611 // which should include file system full errors.
1618 lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1620 lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1630 string const Buffer::asciiParagraph(Paragraph const * par,
1631 unsigned int linelen) const
1633 ostringstream buffer;
1634 Paragraph::depth_type depth = 0;
1636 Paragraph::depth_type ltype_depth = 0;
1637 unsigned int currlinelen = 0;
1638 bool ref_printed = false;
1642 if (!par->previous()) {
1643 // begins or ends a deeper area ?
1644 if (depth != par->params().depth()) {
1645 if (par->params().depth() > depth) {
1646 while (par->params().depth() > depth) {
1650 while (par->params().depth() < depth) {
1656 // First write the layout
1657 string const tmp = textclasslist.NameOfLayout(params.textclass, par->layout);
1658 if (tmp == "Itemize") {
1660 ltype_depth = depth + 1;
1661 } else if (tmp == "Enumerate") {
1663 ltype_depth = depth + 1;
1664 } else if (contains(tmp, "ection")) {
1666 ltype_depth = depth + 1;
1667 } else if (contains(tmp, "aragraph")) {
1669 ltype_depth = depth + 1;
1670 } else if (tmp == "Description") {
1672 ltype_depth = depth + 1;
1673 } else if (tmp == "Abstract") {
1676 } else if (tmp == "Bibliography") {
1684 /* maybe some vertical spaces */
1686 /* the labelwidthstring used in lists */
1690 /* some pagebreaks? */
1694 /* what about the alignment */
1696 lyxerr << "Should this ever happen?" << endl;
1700 LyXFont const font1 = LyXFont(LyXFont::ALL_INHERIT, params.language);
1702 for (Paragraph::size_type i = 0; i < par->size(); ++i) {
1703 if (!i && !noparbreak) {
1706 for (Paragraph::depth_type j = 0; j < depth; ++j)
1708 currlinelen = depth * 2;
1711 case 4: // (Sub)Paragraph
1712 case 5: // Description
1716 buffer << "Abstract\n\n";
1718 buffer << "Abstract: ";
1720 case 7: // Bibliography
1723 buffer << "References\n\n";
1725 buffer << "References: ";
1730 buffer << par->params().labelString() << " ";
1733 if (ltype_depth > depth) {
1734 for (Paragraph::depth_type j = ltype_depth - 1;
1737 currlinelen += (ltype_depth-depth)*2;
1741 LyXFont const font2 = par->getFontSettings(params, i);
1742 if (font1.latex() != font2.latex()) {
1743 if (font2.latex() == LyXFont::OFF)
1752 char c = par->getUChar(params, i);
1756 case Paragraph::META_INSET:
1758 Inset const * inset = par->getInset(i);
1760 if (!inset->ascii(this, buffer)) {
1763 rsplit(buffer.str().c_str(),
1765 currlinelen += s.length();
1767 // to be sure it breaks paragraph
1768 currlinelen += linelen;
1774 case Paragraph::META_NEWLINE:
1777 for (Paragraph::depth_type j = 0;
1781 currlinelen = depth * 2;
1782 if (ltype_depth > depth) {
1783 for (Paragraph::depth_type j = ltype_depth;
1786 currlinelen += (ltype_depth - depth) * 2;
1790 case Paragraph::META_HFILL:
1799 if ((linelen > 0) && (currlinelen > (linelen - 10)) &&
1800 (c == ' ') && ((i + 2) < par->size()))
1803 for (Paragraph::depth_type j = 0;
1806 currlinelen = depth * 2;
1807 if (ltype_depth > depth) {
1808 for (Paragraph::depth_type j = ltype_depth;
1811 currlinelen += (ltype_depth-depth)*2;
1813 } else if (c != '\0')
1816 lyxerr[Debug::INFO] << "writeAsciiFile: NULL char in structure." << endl;
1821 return buffer.str().c_str();
1825 void Buffer::writeFileAscii(string const & fname, int linelen)
1827 ofstream ofs(fname.c_str());
1829 WriteFSAlert(_("Error: Cannot write file:"), fname);
1832 writeFileAscii(ofs, linelen);
1836 void Buffer::writeFileAscii(ostream & ofs, int linelen)
1838 Paragraph * par = paragraph;
1840 ofs << asciiParagraph(par, linelen);
1848 void Buffer::makeLaTeXFile(string const & fname,
1849 string const & original_path,
1850 bool nice, bool only_body)
1852 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
1854 niceFile = nice; // this will be used by Insetincludes.
1856 tex_code_break_column = lyxrc.ascii_linelen;
1858 LyXTextClass const & tclass =
1859 textclasslist.TextClass(params.textclass);
1861 ofstream ofs(fname.c_str());
1863 WriteFSAlert(_("Error: Cannot open file: "), fname);
1867 // validate the buffer.
1868 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
1869 LaTeXFeatures features(params, tclass.numLayouts());
1871 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
1874 // The starting paragraph of the coming rows is the
1875 // first paragraph of the document. (Asger)
1876 texrow.start(paragraph, 0);
1878 if (!only_body && nice) {
1879 ofs << "%% " LYX_DOCVERSION " created this file. "
1880 "For more info, see http://www.lyx.org/.\n"
1881 "%% Do not edit unless you really know what "
1886 lyxerr[Debug::INFO] << "lyx header finished" << endl;
1887 // There are a few differences between nice LaTeX and usual files:
1888 // usual is \batchmode and has a
1889 // special input@path to allow the including of figures
1890 // with either \input or \includegraphics (what figinsets do).
1891 // batchmode is not set if there is a tex_code_break_column.
1892 // In this case somebody is interested in the generated LaTeX,
1893 // so this is OK. input@path is set when the actual parameter
1894 // original_path is set. This is done for usual tex-file, but not
1895 // for nice-latex-file. (Matthias 250696)
1898 // code for usual, NOT nice-latex-file
1899 ofs << "\\batchmode\n"; // changed
1900 // from \nonstopmode
1903 if (!original_path.empty()) {
1904 ofs << "\\makeatletter\n"
1905 << "\\def\\input@path{{"
1906 << os::external_path(original_path) << "/}}\n"
1907 << "\\makeatother\n";
1913 ofs << "\\documentclass";
1915 ostringstream options; // the document class options.
1917 if (tokenPos(tclass.opt_fontsize(),
1918 '|', params.fontsize) >= 0) {
1919 // only write if existing in list (and not default)
1920 options << params.fontsize << "pt,";
1924 if (!params.use_geometry &&
1925 (params.paperpackage == BufferParams::PACKAGE_NONE)) {
1926 switch (params.papersize) {
1927 case BufferParams::PAPER_A4PAPER:
1928 options << "a4paper,";
1930 case BufferParams::PAPER_USLETTER:
1931 options << "letterpaper,";
1933 case BufferParams::PAPER_A5PAPER:
1934 options << "a5paper,";
1936 case BufferParams::PAPER_B5PAPER:
1937 options << "b5paper,";
1939 case BufferParams::PAPER_EXECUTIVEPAPER:
1940 options << "executivepaper,";
1942 case BufferParams::PAPER_LEGALPAPER:
1943 options << "legalpaper,";
1949 if (params.sides != tclass.sides()) {
1950 switch (params.sides) {
1951 case LyXTextClass::OneSide:
1952 options << "oneside,";
1954 case LyXTextClass::TwoSides:
1955 options << "twoside,";
1961 if (params.columns != tclass.columns()) {
1962 if (params.columns == 2)
1963 options << "twocolumn,";
1965 options << "onecolumn,";
1968 if (!params.use_geometry
1969 && params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
1970 options << "landscape,";
1972 // language should be a parameter to \documentclass
1974 ostringstream language_options;
1975 if (params.language->babel() == "hebrew"
1976 && default_language->babel() != "hebrew")
1977 // This seems necessary
1978 features.UsedLanguages.insert(default_language);
1980 if (lyxrc.language_use_babel ||
1981 params.language->lang() != lyxrc.default_language ||
1982 !features.UsedLanguages.empty()) {
1984 for (LaTeXFeatures::LanguageList::const_iterator cit =
1985 features.UsedLanguages.begin();
1986 cit != features.UsedLanguages.end(); ++cit)
1987 language_options << (*cit)->babel() << ',';
1988 language_options << params.language->babel();
1989 if (lyxrc.language_global_options)
1990 options << language_options.str() << ',';
1993 // the user-defined options
1994 if (!params.options.empty()) {
1995 options << params.options << ',';
1998 string strOptions(options.str().c_str());
1999 if (!strOptions.empty()){
2000 strOptions = strip(strOptions, ',');
2001 ofs << '[' << strOptions << ']';
2005 << textclasslist.LatexnameOfClass(params.textclass)
2008 // end of \documentclass defs
2010 // font selection must be done before loading fontenc.sty
2011 // The ae package is not needed when using OT1 font encoding.
2012 if (params.fonts != "default" &&
2013 (params.fonts != "ae" || lyxrc.fontenc != "default")) {
2014 ofs << "\\usepackage{" << params.fonts << "}\n";
2016 if (params.fonts == "ae") {
2017 ofs << "\\usepackage{aecompl}\n";
2021 // this one is not per buffer
2022 if (lyxrc.fontenc != "default") {
2023 ofs << "\\usepackage[" << lyxrc.fontenc
2028 if (params.inputenc == "auto") {
2029 string const doc_encoding =
2030 params.language->encoding()->LatexName();
2032 // Create a list with all the input encodings used
2034 set<string> encodings;
2035 for (LaTeXFeatures::LanguageList::const_iterator it =
2036 features.UsedLanguages.begin();
2037 it != features.UsedLanguages.end(); ++it)
2038 if ((*it)->encoding()->LatexName() != doc_encoding)
2039 encodings.insert((*it)->encoding()->LatexName());
2041 ofs << "\\usepackage[";
2042 std::copy(encodings.begin(), encodings.end(),
2043 std::ostream_iterator<string>(ofs, ","));
2044 ofs << doc_encoding << "]{inputenc}\n";
2046 } else if (params.inputenc != "default") {
2047 ofs << "\\usepackage[" << params.inputenc
2052 // At the very beginning the text parameters.
2053 if (params.paperpackage != BufferParams::PACKAGE_NONE) {
2054 switch (params.paperpackage) {
2055 case BufferParams::PACKAGE_A4:
2056 ofs << "\\usepackage{a4}\n";
2059 case BufferParams::PACKAGE_A4WIDE:
2060 ofs << "\\usepackage{a4wide}\n";
2063 case BufferParams::PACKAGE_WIDEMARGINSA4:
2064 ofs << "\\usepackage[widemargins]{a4}\n";
2069 if (params.use_geometry) {
2070 ofs << "\\usepackage{geometry}\n";
2072 ofs << "\\geometry{verbose";
2073 if (params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
2074 ofs << ",landscape";
2075 switch (params.papersize2) {
2076 case BufferParams::VM_PAPER_CUSTOM:
2077 if (!params.paperwidth.empty())
2078 ofs << ",paperwidth="
2079 << params.paperwidth;
2080 if (!params.paperheight.empty())
2081 ofs << ",paperheight="
2082 << params.paperheight;
2084 case BufferParams::VM_PAPER_USLETTER:
2085 ofs << ",letterpaper";
2087 case BufferParams::VM_PAPER_USLEGAL:
2088 ofs << ",legalpaper";
2090 case BufferParams::VM_PAPER_USEXECUTIVE:
2091 ofs << ",executivepaper";
2093 case BufferParams::VM_PAPER_A3:
2096 case BufferParams::VM_PAPER_A4:
2099 case BufferParams::VM_PAPER_A5:
2102 case BufferParams::VM_PAPER_B3:
2105 case BufferParams::VM_PAPER_B4:
2108 case BufferParams::VM_PAPER_B5:
2112 // default papersize ie BufferParams::VM_PAPER_DEFAULT
2113 switch (lyxrc.default_papersize) {
2114 case BufferParams::PAPER_DEFAULT: // keep compiler happy
2115 case BufferParams::PAPER_USLETTER:
2116 ofs << ",letterpaper";
2118 case BufferParams::PAPER_LEGALPAPER:
2119 ofs << ",legalpaper";
2121 case BufferParams::PAPER_EXECUTIVEPAPER:
2122 ofs << ",executivepaper";
2124 case BufferParams::PAPER_A3PAPER:
2127 case BufferParams::PAPER_A4PAPER:
2130 case BufferParams::PAPER_A5PAPER:
2133 case BufferParams::PAPER_B5PAPER:
2138 if (!params.topmargin.empty())
2139 ofs << ",tmargin=" << params.topmargin;
2140 if (!params.bottommargin.empty())
2141 ofs << ",bmargin=" << params.bottommargin;
2142 if (!params.leftmargin.empty())
2143 ofs << ",lmargin=" << params.leftmargin;
2144 if (!params.rightmargin.empty())
2145 ofs << ",rmargin=" << params.rightmargin;
2146 if (!params.headheight.empty())
2147 ofs << ",headheight=" << params.headheight;
2148 if (!params.headsep.empty())
2149 ofs << ",headsep=" << params.headsep;
2150 if (!params.footskip.empty())
2151 ofs << ",footskip=" << params.footskip;
2155 if (features.amsstyle
2156 && !tclass.provides(LyXTextClass::amsmath)) {
2157 ofs << "\\usepackage{amsmath}\n";
2161 if (tokenPos(tclass.opt_pagestyle(),
2162 '|', params.pagestyle) >= 0) {
2163 if (params.pagestyle == "fancy") {
2164 ofs << "\\usepackage{fancyhdr}\n";
2167 ofs << "\\pagestyle{" << params.pagestyle << "}\n";
2171 // We try to load babel late, in case it interferes
2172 // with other packages.
2174 string tmp = lyxrc.language_package;
2175 if (!lyxrc.language_global_options
2176 && tmp == "\\usepackage{babel}")
2177 tmp = string("\\usepackage[") +
2178 language_options.str().c_str() +
2184 if (params.secnumdepth != tclass.secnumdepth()) {
2185 ofs << "\\setcounter{secnumdepth}{"
2186 << params.secnumdepth
2190 if (params.tocdepth != tclass.tocdepth()) {
2191 ofs << "\\setcounter{tocdepth}{"
2197 if (params.paragraph_separation) {
2198 switch (params.defskip.kind()) {
2199 case VSpace::SMALLSKIP:
2200 ofs << "\\setlength\\parskip{\\smallskipamount}\n";
2202 case VSpace::MEDSKIP:
2203 ofs << "\\setlength\\parskip{\\medskipamount}\n";
2205 case VSpace::BIGSKIP:
2206 ofs << "\\setlength\\parskip{\\bigskipamount}\n";
2208 case VSpace::LENGTH:
2209 ofs << "\\setlength\\parskip{"
2210 << params.defskip.length().asLatexString()
2213 default: // should never happen // Then delete it.
2214 ofs << "\\setlength\\parskip{\\medskipamount}\n";
2219 ofs << "\\setlength\\parindent{0pt}\n";
2223 // Now insert the LyX specific LaTeX commands...
2225 // The optional packages;
2226 string preamble(features.getPackages());
2228 // this might be useful...
2229 preamble += "\n\\makeatletter\n";
2231 // Some macros LyX will need
2232 string tmppreamble(features.getMacros());
2234 if (!tmppreamble.empty()) {
2235 preamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2236 "LyX specific LaTeX commands.\n"
2237 + tmppreamble + '\n';
2240 // the text class specific preamble
2241 tmppreamble = features.getTClassPreamble();
2242 if (!tmppreamble.empty()) {
2243 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2244 "Textclass specific LaTeX commands.\n"
2245 + tmppreamble + '\n';
2248 /* the user-defined preamble */
2249 if (!params.preamble.empty()) {
2250 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2251 "User specified LaTeX commands.\n"
2252 + params.preamble + '\n';
2255 preamble += "\\makeatother\n";
2257 // Itemize bullet settings need to be last in case the user
2258 // defines their own bullets that use a package included
2259 // in the user-defined preamble -- ARRae
2260 // Actually it has to be done much later than that
2261 // since some packages like frenchb make modifications
2262 // at \begin{document} time -- JMarc
2264 for (int i = 0; i < 4; ++i) {
2265 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
2266 if (bullets_def.empty())
2267 bullets_def="\\AtBeginDocument{\n";
2268 bullets_def += " \\renewcommand{\\labelitemi";
2270 // `i' is one less than the item to modify
2277 bullets_def += "ii";
2283 bullets_def += "}{" +
2284 params.user_defined_bullets[i].getText()
2289 if (!bullets_def.empty())
2290 preamble += bullets_def + "}\n\n";
2292 for (int j = countChar(preamble, '\n'); j-- ;) {
2299 ofs << "\\begin{document}\n";
2302 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
2304 if (!lyxrc.language_auto_begin) {
2305 ofs << subst(lyxrc.language_command_begin, "$$lang",
2306 params.language->babel())
2311 latexParagraphs(ofs, paragraph, 0, texrow);
2313 // add this just in case after all the paragraphs
2317 if (!lyxrc.language_auto_end) {
2318 ofs << subst(lyxrc.language_command_end, "$$lang",
2319 params.language->babel())
2325 ofs << "\\end{document}\n";
2328 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
2330 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
2334 // Just to be sure. (Asger)
2337 // tex_code_break_column's value is used to decide
2338 // if we are in batchmode or not (within mathed_write()
2339 // in math_write.C) so we must set it to a non-zero
2340 // value when we leave otherwise we save incorrect .lyx files.
2341 tex_code_break_column = lyxrc.ascii_linelen;
2345 lyxerr << "File was not closed properly." << endl;
2348 lyxerr[Debug::INFO] << "Finished making latex file." << endl;
2353 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
2355 void Buffer::latexParagraphs(ostream & ofs, Paragraph * par,
2356 Paragraph * endpar, TexRow & texrow) const
2358 bool was_title = false;
2359 bool already_title = false;
2362 while (par != endpar) {
2363 LyXLayout const & layout =
2364 textclasslist.Style(params.textclass,
2367 if (layout.intitle) {
2368 if (already_title) {
2369 lyxerr <<"Error in latexParagraphs: You"
2370 " should not mix title layouts"
2371 " with normal ones." << endl;
2374 } else if (was_title && !already_title) {
2375 ofs << "\\maketitle\n";
2377 already_title = true;
2381 if (layout.isEnvironment()) {
2382 par = par->TeXEnvironment(this, params, ofs, texrow);
2384 par = par->TeXOnePar(this, params, ofs, texrow, false);
2387 // It might be that we only have a title in this document
2388 if (was_title && !already_title) {
2389 ofs << "\\maketitle\n";
2395 bool Buffer::isLatex() const
2397 return textclasslist.TextClass(params.textclass).outputType() == LATEX;
2401 bool Buffer::isLinuxDoc() const
2403 return textclasslist.TextClass(params.textclass).outputType() == LINUXDOC;
2407 bool Buffer::isLiterate() const
2409 return textclasslist.TextClass(params.textclass).outputType() == LITERATE;
2413 bool Buffer::isDocBook() const
2415 return textclasslist.TextClass(params.textclass).outputType() == DOCBOOK;
2419 bool Buffer::isSGML() const
2421 return textclasslist.TextClass(params.textclass).outputType() == LINUXDOC ||
2422 textclasslist.TextClass(params.textclass).outputType() == DOCBOOK;
2426 void Buffer::sgmlOpenTag(ostream & os, Paragraph::depth_type depth,
2427 string const & latexname) const
2429 if (!latexname.empty() && latexname != "!-- --")
2430 os << "<!-- " << depth << " -->" << "<" << latexname << ">";
2431 //os << string(depth, ' ') << "<" << latexname << ">\n";
2435 void Buffer::sgmlCloseTag(ostream & os, Paragraph::depth_type depth,
2436 string const & latexname) const
2438 if (!latexname.empty() && latexname != "!-- --")
2439 os << "<!-- " << depth << " -->" << "</" << latexname << ">\n";
2440 //os << string(depth, ' ') << "</" << latexname << ">\n";
2444 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
2446 ofstream ofs(fname.c_str());
2449 WriteAlert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2453 niceFile = nice; // this will be used by included files.
2455 LyXTextClass const & tclass =
2456 textclasslist.TextClass(params.textclass);
2458 LaTeXFeatures features(params, tclass.numLayouts());
2463 string top_element = textclasslist.LatexnameOfClass(params.textclass);
2466 string sgml_includedfiles=features.getIncludedFiles(fname);
2468 if (params.preamble.empty() && sgml_includedfiles.empty()) {
2469 ofs << "<!doctype linuxdoc system>\n\n";
2471 ofs << "<!doctype linuxdoc system [ "
2472 << params.preamble << sgml_includedfiles << " \n]>\n\n";
2475 if (params.options.empty())
2476 sgmlOpenTag(ofs, 0, top_element);
2478 string top = top_element;
2480 top += params.options;
2481 sgmlOpenTag(ofs, 0, top);
2485 ofs << "<!-- " << LYX_DOCVERSION
2486 << " created this file. For more info see http://www.lyx.org/"
2489 Paragraph::depth_type depth = 0; // paragraph depth
2490 Paragraph * par = paragraph;
2492 vector<string> environment_stack(5);
2495 LyXLayout const & style =
2496 textclasslist.Style(params.textclass,
2499 // treat <toc> as a special case for compatibility with old code
2500 if (par->getChar(0) == Paragraph::META_INSET) {
2501 Inset * inset = par->getInset(0);
2502 Inset::Code lyx_code = inset->lyxCode();
2503 if (lyx_code == Inset::TOC_CODE){
2504 string const temp = "toc";
2505 sgmlOpenTag(ofs, depth, temp);
2512 // environment tag closing
2513 for (; depth > par->params().depth(); --depth) {
2514 sgmlCloseTag(ofs, depth, environment_stack[depth]);
2515 environment_stack[depth].erase();
2518 // write opening SGML tags
2519 switch (style.latextype) {
2520 case LATEX_PARAGRAPH:
2521 if (depth == par->params().depth()
2522 && !environment_stack[depth].empty()) {
2523 sgmlCloseTag(ofs, depth, environment_stack[depth]);
2524 environment_stack[depth].erase();
2530 sgmlOpenTag(ofs, depth, style.latexname());
2535 linuxDocError(par, 0,
2536 _("Error : Wrong depth for"
2537 " LatexType Command.\n"));
2539 if (!environment_stack[depth].empty()){
2540 sgmlCloseTag(ofs, depth,
2541 environment_stack[depth]);
2545 environment_stack[depth].erase();
2546 sgmlOpenTag(ofs, depth, style.latexname());
2549 case LATEX_ENVIRONMENT:
2550 case LATEX_ITEM_ENVIRONMENT:
2551 if (depth == par->params().depth()
2552 && environment_stack[depth] != style.latexname()) {
2553 sgmlCloseTag(ofs, depth,
2554 environment_stack[depth]);
2555 environment_stack[depth].erase();
2557 if (depth < par->params().depth()) {
2558 depth = par->params().depth();
2559 environment_stack[depth].erase();
2561 if (environment_stack[depth] != style.latexname()) {
2563 sgmlOpenTag(ofs, depth, "p");
2565 sgmlOpenTag(ofs, depth, style.latexname());
2567 if (environment_stack.size() == depth + 1)
2568 environment_stack.push_back("!-- --");
2569 environment_stack[depth] = style.latexname();
2572 if (style.latexparam() == "CDATA")
2575 if (style.latextype == LATEX_ENVIRONMENT) break;
2577 if (style.labeltype == LABEL_MANUAL)
2582 sgmlOpenTag(ofs, depth + 1, item_name);
2585 sgmlOpenTag(ofs, depth, style.latexname());
2589 simpleLinuxDocOnePar(ofs, par, depth);
2594 // write closing SGML tags
2595 switch (style.latextype) {
2598 case LATEX_ENVIRONMENT:
2599 case LATEX_ITEM_ENVIRONMENT:
2600 if (style.latexparam() == "CDATA")
2604 sgmlCloseTag(ofs, depth, style.latexname());
2610 for (int i=depth; i >= 0; --i)
2611 sgmlCloseTag(ofs, depth, environment_stack[i]);
2615 sgmlCloseTag(ofs, 0, top_element);
2619 // How to check for successful close
2623 void Buffer::docBookHandleCaption(ostream & os, string & inner_tag,
2624 Paragraph::depth_type depth, int desc_on,
2627 Paragraph * tpar = par;
2629 && (tpar->layout != textclasslist.NumberOfLayout(params.textclass,
2631 tpar = tpar->next();
2634 tpar->layout == textclasslist.NumberOfLayout(params.textclass,
2635 "Caption").second) {
2636 sgmlOpenTag(os, depth + 1, inner_tag);
2638 simpleDocBookOnePar(os, extra_par, tpar,
2639 desc_on, depth + 2);
2640 sgmlCloseTag(os, depth+1, inner_tag);
2641 if (!extra_par.empty())
2647 // checks, if newcol chars should be put into this line
2648 // writes newline, if necessary.
2651 void linux_doc_line_break(ostream & os, string::size_type & colcount,
2652 string::size_type newcol)
2655 if (colcount > lyxrc.ascii_linelen) {
2657 colcount = newcol; // assume write after this call
2672 string tag_name(PAR_TAG const & pt) {
2674 case NONE: return "!-- --";
2675 case TT: return "tt";
2676 case SF: return "sf";
2677 case BF: return "bf";
2678 case IT: return "it";
2679 case SL: return "sl";
2680 case EM: return "em";
2687 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2689 p1 = static_cast<PAR_TAG>(p1 | p2);
2694 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2696 p1 = static_cast<PAR_TAG>( p1 & ~p2);
2704 // Handle internal paragraph parsing -- layout already processed.
2705 void Buffer::simpleLinuxDocOnePar(ostream & os,
2707 Paragraph::depth_type /*depth*/)
2709 LyXLayout const & style = textclasslist.Style(params.textclass,
2711 string::size_type char_line_count = 5; // Heuristic choice ;-)
2713 // gets paragraph main font
2716 if (style.labeltype == LABEL_MANUAL) {
2717 font_old = style.labelfont;
2720 font_old = style.font;
2724 LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
2725 LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
2726 LyXFont::FONT_SHAPE shape_type = LyXFont::UP_SHAPE;
2729 stack < PAR_TAG > tag_state;
2730 // parsing main loop
2731 for (Paragraph::size_type i = 0; i < par->size(); ++i) {
2733 PAR_TAG tag_close = NONE;
2734 list < PAR_TAG > tag_open;
2736 LyXFont const font = par->getFont(params, i);
2738 if (font_old.family() != font.family()) {
2739 switch (family_type) {
2740 case LyXFont::SANS_FAMILY:
2743 case LyXFont::TYPEWRITER_FAMILY:
2750 family_type = font.family();
2752 switch (family_type) {
2753 case LyXFont::SANS_FAMILY:
2754 tag_open.push_back(SF);
2756 case LyXFont::TYPEWRITER_FAMILY:
2757 tag_open.push_back(TT);
2764 if (font_old.series() != font.series()) {
2765 switch (series_type) {
2766 case LyXFont::BOLD_SERIES:
2773 series_type = font.series();
2775 switch (series_type) {
2776 case LyXFont::BOLD_SERIES:
2777 tag_open.push_back(BF);
2785 if (font_old.shape() != font.shape()) {
2786 switch (shape_type) {
2787 case LyXFont::ITALIC_SHAPE:
2790 case LyXFont::SLANTED_SHAPE:
2797 shape_type = font.shape();
2799 switch (shape_type) {
2800 case LyXFont::ITALIC_SHAPE:
2801 tag_open.push_back(IT);
2803 case LyXFont::SLANTED_SHAPE:
2804 tag_open.push_back(SL);
2811 if (font_old.emph() != font.emph()) {
2812 if (font.emph() == LyXFont::ON) {
2813 tag_open.push_back(EM);
2822 list < PAR_TAG > temp;
2823 while(!tag_state.empty() && tag_close ) {
2824 PAR_TAG k = tag_state.top();
2826 os << "</" << tag_name(k) << ">";
2833 for(list< PAR_TAG >::const_iterator j = temp.begin();
2834 j != temp.end(); ++j) {
2836 os << "<" << tag_name(*j) << ">";
2839 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
2840 j != tag_open.end(); ++j) {
2842 os << "<" << tag_name(*j) << ">";
2845 char c = par->getChar(i);
2847 if (c == Paragraph::META_INSET) {
2848 Inset * inset = par->getInset(i);
2849 inset->linuxdoc(this, os);
2856 font.latex() == LyXFont::ON ||
2858 style.latexparam() == "CDATA") {
2859 // "TeX"-Mode on == > SGML-Mode on.
2865 if (par->linuxDocConvertChar(c, sgml_string)
2866 && !style.free_spacing) {
2867 // in freespacing mode, spaces are
2868 // non-breaking characters
2869 if (desc_on) {// if char is ' ' then...
2872 linux_doc_line_break(os, char_line_count, 6);
2876 linux_doc_line_break(os, char_line_count, 1);
2881 char_line_count += sgml_string.length();
2887 while (!tag_state.empty()) {
2888 os << "</" << tag_name(tag_state.top()) << ">";
2892 // resets description flag correctly
2894 // <tag> not closed...
2895 linux_doc_line_break(os, char_line_count, 6);
2901 // Print an error message.
2902 void Buffer::linuxDocError(Paragraph * par, int pos,
2903 string const & message)
2905 // insert an error marker in text
2906 InsetError * new_inset = new InsetError(message);
2907 par->insertInset(pos, new_inset);
2911 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
2913 ofstream ofs(fname.c_str());
2915 WriteAlert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2919 Paragraph * par = paragraph;
2921 niceFile = nice; // this will be used by Insetincludes.
2923 LyXTextClass const & tclass =
2924 textclasslist.TextClass(params.textclass);
2926 LaTeXFeatures features(params, tclass.numLayouts());
2931 string top_element = textclasslist.LatexnameOfClass(params.textclass);
2934 string sgml_includedfiles = features.getIncludedFiles(fname);
2936 ofs << "<!doctype " << top_element
2937 << " public \"-//OASIS//DTD DocBook V3.1//EN\"";
2939 if (params.preamble.empty() && sgml_includedfiles.empty())
2942 ofs << "\n [ " << params.preamble
2943 << sgml_includedfiles << " \n]>\n\n";
2946 string top = top_element;
2948 top += params.language->code();
2951 if (!params.options.empty()) {
2953 top += params.options;
2955 sgmlOpenTag(ofs, 0, top);
2957 ofs << "<!-- DocBook file was created by " << LYX_DOCVERSION
2958 << "\n See http://www.lyx.org/ for more information -->\n";
2960 vector<string> environment_stack(10);
2961 vector<string> environment_inner(10);
2962 vector<string> command_stack(10);
2964 bool command_flag = false;
2965 Paragraph::depth_type command_depth = 0;
2966 Paragraph::depth_type command_base = 0;
2967 Paragraph::depth_type cmd_depth = 0;
2968 Paragraph::depth_type depth = 0; // paragraph depth
2971 string command_name;
2977 int desc_on = 0; // description mode
2979 LyXLayout const & style =
2980 textclasslist.Style(params.textclass,
2983 // environment tag closing
2984 for (; depth > par->params().depth(); --depth) {
2985 if (environment_inner[depth] != "!-- --") {
2986 item_name = "listitem";
2987 sgmlCloseTag(ofs, command_depth + depth,
2989 if (environment_inner[depth] == "varlistentry")
2990 sgmlCloseTag(ofs, depth+command_depth,
2991 environment_inner[depth]);
2993 sgmlCloseTag(ofs, depth + command_depth,
2994 environment_stack[depth]);
2995 environment_stack[depth].erase();
2996 environment_inner[depth].erase();
2999 if (depth == par->params().depth()
3000 && environment_stack[depth] != style.latexname()
3001 && !environment_stack[depth].empty()) {
3002 if (environment_inner[depth] != "!-- --") {
3003 item_name= "listitem";
3004 sgmlCloseTag(ofs, command_depth+depth,
3006 if (environment_inner[depth] == "varlistentry")
3008 depth + command_depth,
3009 environment_inner[depth]);
3012 sgmlCloseTag(ofs, depth + command_depth,
3013 environment_stack[depth]);
3015 environment_stack[depth].erase();
3016 environment_inner[depth].erase();
3019 // Write opening SGML tags.
3020 switch (style.latextype) {
3021 case LATEX_PARAGRAPH:
3022 sgmlOpenTag(ofs, depth + command_depth,
3028 linuxDocError(par, 0,
3029 _("Error : Wrong depth for "
3030 "LatexType Command.\n"));
3032 command_name = style.latexname();
3034 sgmlparam = style.latexparam();
3035 c_params = split(sgmlparam, c_depth,'|');
3037 cmd_depth = lyx::atoi(c_depth);
3040 if (cmd_depth < command_base) {
3041 for (Paragraph::depth_type j = command_depth; j >= command_base; --j)
3042 sgmlCloseTag(ofs, j, command_stack[j]);
3043 command_depth = command_base = cmd_depth;
3044 } else if (cmd_depth <= command_depth) {
3045 for (int j = command_depth; j >= int(cmd_depth); --j)
3046 sgmlCloseTag(ofs, j, command_stack[j]);
3047 command_depth = cmd_depth;
3049 command_depth = cmd_depth;
3051 command_depth = command_base = cmd_depth;
3052 command_flag = true;
3054 if (command_stack.size() == command_depth + 1)
3055 command_stack.push_back(string());
3056 command_stack[command_depth] = command_name;
3058 // treat label as a special case for
3059 // more WYSIWYM handling.
3060 if (par->getChar(0) == Paragraph::META_INSET) {
3061 Inset * inset = par->getInset(0);
3062 Inset::Code lyx_code = inset->lyxCode();
3063 if (lyx_code == Inset::LABEL_CODE){
3064 command_name += " id=\"";
3065 command_name += (static_cast<InsetCommand *>(inset))->getContents();
3066 command_name += "\"";
3071 sgmlOpenTag(ofs, depth + command_depth, command_name);
3072 if (c_params.empty())
3073 item_name = "title";
3075 item_name = c_params;
3076 sgmlOpenTag(ofs, depth + 1 + command_depth, item_name);
3079 case LATEX_ENVIRONMENT:
3080 case LATEX_ITEM_ENVIRONMENT:
3081 if (depth < par->params().depth()) {
3082 depth = par->params().depth();
3083 environment_stack[depth].erase();
3086 if (environment_stack[depth] != style.latexname()) {
3087 if(environment_stack.size() == depth + 1) {
3088 environment_stack.push_back("!-- --");
3089 environment_inner.push_back("!-- --");
3091 environment_stack[depth] = style.latexname();
3092 environment_inner[depth] = "!-- --";
3093 sgmlOpenTag(ofs, depth + command_depth,
3094 environment_stack[depth]);
3096 if (environment_inner[depth] != "!-- --") {
3097 item_name= "listitem";
3099 command_depth + depth,
3101 if (environment_inner[depth] == "varlistentry")
3103 depth + command_depth,
3104 environment_inner[depth]);
3108 if (style.latextype == LATEX_ENVIRONMENT) {
3109 if (!style.latexparam().empty()) {
3110 if(style.latexparam() == "CDATA")
3113 sgmlOpenTag(ofs, depth + command_depth,
3114 style.latexparam());
3119 desc_on = (style.labeltype == LABEL_MANUAL);
3122 environment_inner[depth]= "varlistentry";
3124 environment_inner[depth]= "listitem";
3126 sgmlOpenTag(ofs, depth + 1 + command_depth,
3127 environment_inner[depth]);
3131 sgmlOpenTag(ofs, depth + 1 + command_depth,
3135 sgmlOpenTag(ofs, depth + 1 + command_depth,
3140 sgmlOpenTag(ofs, depth + command_depth,
3146 simpleDocBookOnePar(ofs, extra_par, par, desc_on,
3147 depth + 1 + command_depth);
3151 // write closing SGML tags
3152 switch (style.latextype) {
3154 if (c_params.empty())
3158 sgmlCloseTag(ofs, depth + command_depth, end_tag);
3160 case LATEX_ENVIRONMENT:
3161 if (!style.latexparam().empty()) {
3162 if(style.latexparam() == "CDATA")
3165 sgmlCloseTag(ofs, depth + command_depth,
3166 style.latexparam());
3169 case LATEX_ITEM_ENVIRONMENT:
3170 if (desc_on == 1) break;
3172 sgmlCloseTag(ofs, depth + 1 + command_depth, end_tag);
3174 case LATEX_PARAGRAPH:
3175 sgmlCloseTag(ofs, depth + command_depth, style.latexname());
3178 sgmlCloseTag(ofs, depth + command_depth, style.latexname());
3184 for (int d = depth; d >= 0; --d) {
3185 if (!environment_stack[depth].empty()) {
3186 if (environment_inner[depth] != "!-- --") {
3187 item_name = "listitem";
3188 sgmlCloseTag(ofs, command_depth + depth,
3190 if (environment_inner[depth] == "varlistentry")
3191 sgmlCloseTag(ofs, depth + command_depth,
3192 environment_inner[depth]);
3195 sgmlCloseTag(ofs, depth + command_depth,
3196 environment_stack[depth]);
3200 for (int j = command_depth; j >= 0 ; --j)
3201 if (!command_stack[j].empty())
3202 sgmlCloseTag(ofs, j, command_stack[j]);
3205 sgmlCloseTag(ofs, 0, top_element);
3208 // How to check for successful close
3212 void Buffer::simpleDocBookOnePar(ostream & os, string & extra,
3213 Paragraph * par, int & desc_on,
3214 Paragraph::depth_type depth) const
3216 bool emph_flag = false;
3218 LyXLayout const & style = textclasslist.Style(params.textclass,
3221 LyXFont font_old = style.labeltype == LABEL_MANUAL ? style.labelfont : style.font;
3223 int char_line_count = depth;
3224 //if (!style.free_spacing)
3225 // os << string(depth,' ');
3227 // parsing main loop
3228 for (Paragraph::size_type i = 0;
3229 i < par->size(); ++i) {
3230 LyXFont font = par->getFont(params, i);
3232 // handle <emphasis> tag
3233 if (font_old.emph() != font.emph()) {
3234 if (font.emph() == LyXFont::ON) {
3238 os << "</emphasis>";
3243 char c = par->getChar(i);
3245 if (c == Paragraph::META_INSET) {
3246 Inset * inset = par->getInset(i);
3247 std::ostringstream ost;
3248 inset->docBook(this, ost);
3249 string tmp_out = ost.str().c_str();
3252 // This code needs some explanation:
3253 // Two insets are treated specially
3254 // label if it is the first element in a command paragraph
3256 // graphics inside tables or figure floats can't go on
3257 // title (the equivalente in latex for this case is caption
3258 // and title should come first
3261 if (desc_on!= 3 || i!= 0) {
3262 if (!tmp_out.empty() && tmp_out[0] == '@') {
3264 extra += frontStrip(tmp_out, '@');
3266 os << frontStrip(tmp_out, '@');
3272 } else if (font.latex() == LyXFont::ON) {
3273 // "TeX"-Mode on ==> SGML-Mode on.
3280 if (par->linuxDocConvertChar(c, sgml_string)
3281 && !style.free_spacing) { // in freespacing
3283 // non-breaking characters
3287 os << "\n</term><listitem><para>";
3300 os << "</emphasis>";
3303 // resets description flag correctly
3305 // <term> not closed...
3308 if(style.free_spacing) os << '\n';
3312 // This should be enabled when the Chktex class is implemented. (Asger)
3313 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
3314 // Other flags: -wall -v0 -x
3315 int Buffer::runChktex()
3317 if (!users->text) return 0;
3319 users->owner()->prohibitInput();
3321 // get LaTeX-Filename
3322 string const name = getLatexName();
3323 string path = OnlyPath(filename);
3325 string const org_path = path;
3326 if (lyxrc.use_tempdir || (IsDirWriteable(path) < 1)) {
3330 Path p(path); // path to LaTeX file
3331 users->owner()->message(_("Running chktex..."));
3333 // Remove all error insets
3334 bool const removedErrorInsets = users->removeAutoInsets();
3336 // Generate the LaTeX file if neccessary
3337 makeLaTeXFile(name, org_path, false);
3340 Chktex chktex(lyxrc.chktex_command, name, filepath);
3341 int res = chktex.run(terr); // run chktex
3344 WriteAlert(_("chktex did not work!"),
3345 _("Could not run with file:"), name);
3346 } else if (res > 0) {
3347 // Insert all errors as errors boxes
3348 users->insertErrors(terr);
3351 // if we removed error insets before we ran chktex or if we inserted
3352 // error insets after we ran chktex, this must be run:
3353 if (removedErrorInsets || res){
3355 users->fitCursor(users->text);
3357 users->owner()->allowInput();
3363 void Buffer::validate(LaTeXFeatures & features) const
3365 Paragraph * par = paragraph;
3366 LyXTextClass const & tclass =
3367 textclasslist.TextClass(params.textclass);
3369 // AMS Style is at document level
3371 features.amsstyle = (params.use_amsmath ||
3372 tclass.provides(LyXTextClass::amsmath));
3375 // We don't use "lyxerr.debug" because of speed. (Asger)
3376 if (lyxerr.debugging(Debug::LATEX))
3377 lyxerr << "Paragraph: " << par << endl;
3379 // Now just follow the list of paragraphs and run
3380 // validate on each of them.
3381 par->validate(features);
3383 // and then the next paragraph
3387 // the bullet shapes are buffer level not paragraph level
3388 // so they are tested here
3389 for (int i = 0; i < 4; ++i) {
3390 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
3391 int const font = params.user_defined_bullets[i].getFont();
3393 int const c = params
3394 .user_defined_bullets[i]
3401 features.latexsym = true;
3403 } else if (font == 1) {
3404 features.amssymb = true;
3405 } else if ((font >= 2 && font <= 5)) {
3406 features.pifont = true;
3411 if (lyxerr.debugging(Debug::LATEX)) {
3412 features.showStruct();
3417 void Buffer::setPaperStuff()
3419 params.papersize = BufferParams::PAPER_DEFAULT;
3420 char const c1 = params.paperpackage;
3421 if (c1 == BufferParams::PACKAGE_NONE) {
3422 char const c2 = params.papersize2;
3423 if (c2 == BufferParams::VM_PAPER_USLETTER)
3424 params.papersize = BufferParams::PAPER_USLETTER;
3425 else if (c2 == BufferParams::VM_PAPER_USLEGAL)
3426 params.papersize = BufferParams::PAPER_LEGALPAPER;
3427 else if (c2 == BufferParams::VM_PAPER_USEXECUTIVE)
3428 params.papersize = BufferParams::PAPER_EXECUTIVEPAPER;
3429 else if (c2 == BufferParams::VM_PAPER_A3)
3430 params.papersize = BufferParams::PAPER_A3PAPER;
3431 else if (c2 == BufferParams::VM_PAPER_A4)
3432 params.papersize = BufferParams::PAPER_A4PAPER;
3433 else if (c2 == BufferParams::VM_PAPER_A5)
3434 params.papersize = BufferParams::PAPER_A5PAPER;
3435 else if ((c2 == BufferParams::VM_PAPER_B3) || (c2 == BufferParams::VM_PAPER_B4) ||
3436 (c2 == BufferParams::VM_PAPER_B5))
3437 params.papersize = BufferParams::PAPER_B5PAPER;
3438 } else if ((c1 == BufferParams::PACKAGE_A4) || (c1 == BufferParams::PACKAGE_A4WIDE) ||
3439 (c1 == BufferParams::PACKAGE_WIDEMARGINSA4))
3440 params.papersize = BufferParams::PAPER_A4PAPER;
3444 // This function should be in Buffer because it's a buffer's property (ale)
3445 string const Buffer::getIncludeonlyList(char delim)
3448 for (inset_iterator it = inset_iterator_begin();
3449 it != inset_iterator_end(); ++it) {
3450 if ((*it)->lyxCode() == Inset::INCLUDE_CODE) {
3451 InsetInclude * insetinc =
3452 static_cast<InsetInclude *>(*it);
3453 if (insetinc->isIncludeOnly()) {
3456 lst += insetinc->getRelFileBaseName();
3460 lyxerr[Debug::INFO] << "Includeonly(" << lst << ')' << endl;
3465 vector<string> const Buffer::getLabelList()
3467 /// if this is a child document and the parent is already loaded
3468 /// Use the parent's list instead [ale990407]
3469 if (!params.parentname.empty()
3470 && bufferlist.exists(params.parentname)) {
3471 Buffer * tmp = bufferlist.getBuffer(params.parentname);
3473 return tmp->getLabelList();
3476 vector<string> label_list;
3477 for (inset_iterator it = inset_iterator_begin();
3478 it != inset_iterator_end(); ++it) {
3479 vector<string> const l = (*it)->getLabelList();
3480 label_list.insert(label_list.end(), l.begin(), l.end());
3486 Buffer::Lists const Buffer::getLists() const
3489 Paragraph * par = paragraph;
3491 LyXTextClassList::size_type cap;
3492 boost::tie(found, cap) = textclasslist
3493 .NumberOfLayout(params.textclass, "Caption");
3496 char const labeltype =
3497 textclasslist.Style(params.textclass,
3498 par->getLayout()).labeltype;
3500 if (labeltype >= LABEL_COUNTER_CHAPTER
3501 && labeltype <= LABEL_COUNTER_CHAPTER + params.tocdepth) {
3502 // insert this into the table of contents
3503 SingleList & item = l["TOC"];
3506 textclasslist.TextClass(params.textclass).maxcounter());
3507 item.push_back(TocItem(par, depth, par->asString(this, true)));
3509 // For each paragrph, traverse its insets and look for
3513 Paragraph::inset_iterator it =
3514 par->inset_iterator_begin();
3515 Paragraph::inset_iterator end =
3516 par->inset_iterator_end();
3518 for (; it != end; ++it) {
3519 if ((*it)->lyxCode() == Inset::FLOAT_CODE) {
3521 static_cast<InsetFloat*>(*it);
3523 string const type = il->type();
3525 // Now find the caption in the float...
3526 // We now tranverse the paragraphs of
3528 Paragraph * tmp = il->inset.paragraph();
3530 if (tmp->layout == cap) {
3531 SingleList & item = l[type];
3533 tostr(item.size()+1) + ". " + tmp->asString(this, false);
3534 item.push_back(TocItem(tmp, 0 , str));
3541 lyxerr << "caption not found" << endl;
3550 // This is also a buffer property (ale)
3551 vector<pair<string, string> > const Buffer::getBibkeyList()
3553 /// if this is a child document and the parent is already loaded
3554 /// Use the parent's list instead [ale990412]
3555 if (!params.parentname.empty() && bufferlist.exists(params.parentname)) {
3556 Buffer * tmp = bufferlist.getBuffer(params.parentname);
3558 return tmp->getBibkeyList();
3561 vector<pair<string, string> > keys;
3562 Paragraph * par = paragraph;
3565 keys.push_back(pair<string, string>(par->bibkey->getContents(),
3566 par->asString(this, false)));
3570 // Might be either using bibtex or a child has bibliography
3572 for (inset_iterator it = inset_iterator_begin();
3573 it != inset_iterator_end(); ++it) {
3574 // Search for Bibtex or Include inset
3575 if ((*it)->lyxCode() == Inset::BIBTEX_CODE) {
3576 vector<pair<string,string> > tmp =
3577 static_cast<InsetBibtex*>(*it)->getKeys(this);
3578 keys.insert(keys.end(), tmp.begin(), tmp.end());
3579 } else if ((*it)->lyxCode() == Inset::INCLUDE_CODE) {
3580 vector<pair<string,string> > const tmp =
3581 static_cast<InsetInclude*>(*it)->getKeys();
3582 keys.insert(keys.end(), tmp.begin(), tmp.end());
3591 bool Buffer::isDepClean(string const & name) const
3593 DEPCLEAN * item = dep_clean;
3594 while (item && item->master != name)
3596 if (!item) return true;
3601 void Buffer::markDepClean(string const & name)
3604 dep_clean = new DEPCLEAN;
3605 dep_clean->clean = true;
3606 dep_clean->master = name;
3607 dep_clean->next = 0;
3609 DEPCLEAN * item = dep_clean;
3610 while (item && item->master != name)
3615 item = new DEPCLEAN;
3617 item->master = name;
3624 bool Buffer::dispatch(string const & command)
3626 // Split command string into command and argument
3628 string line = frontStrip(command);
3629 string const arg = strip(frontStrip(split(line, cmd, ' ')));
3631 return dispatch(lyxaction.LookupFunc(cmd), arg);
3635 bool Buffer::dispatch(int action, string const & argument)
3637 bool dispatched = true;
3640 Exporter::Export(this, argument, false);
3650 void Buffer::resizeInsets(BufferView * bv)
3652 /// then remove all LyXText in text-insets
3653 Paragraph * par = paragraph;
3654 for (; par; par = par->next()) {
3655 par->resizeInsetsLyXText(bv);
3660 void Buffer::redraw()
3663 users->fitCursor(users->text);
3667 void Buffer::changeLanguage(Language const * from, Language const * to)
3670 Paragraph * par = paragraph;
3672 par->changeLanguage(params, from, to);
3678 bool Buffer::isMultiLingual()
3680 Paragraph * par = paragraph;
3682 if (par->isMultiLingual(params))
3690 Buffer::inset_iterator::inset_iterator(Paragraph * paragraph,
3691 Paragraph::size_type pos)
3694 it = par->InsetIterator(pos);
3695 if (it == par->inset_iterator_end()) {
3702 void Buffer::inset_iterator::setParagraph()
3705 it = par->inset_iterator_begin();
3706 if (it != par->inset_iterator_end())
3711 // We maintain an invariant that whenever par = 0 then it = 0
3715 Inset * Buffer::getInsetFromID(int id_arg) const
3717 for (inset_iterator it = inset_const_iterator_begin();
3718 it != inset_const_iterator_end(); ++it)
3720 if ((*it)->id() == id_arg)
3722 Inset * in = (*it)->getInsetFromID(id_arg);
3730 Paragraph * Buffer::getParFromID(int id) const
3732 if (id < 0) return 0;
3733 Paragraph * par = paragraph;
3735 if (par->id() == id) {
3738 Paragraph * tmp = par->getParFromID(id);