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"
114 using std::make_pair;
122 // all these externs should eventually be removed.
123 extern BufferList bufferlist;
125 extern LyXAction lyxaction;
129 const int LYX_FORMAT = 220;
133 extern int tex_code_break_column;
136 Buffer::Buffer(string const & file, bool ronly)
138 lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
140 filepath = OnlyPath(file);
149 if (read_only || (lyxrc.use_tempdir)) {
150 tmppath = CreateBufferTmpDir();
151 } else tmppath.erase();
157 lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
158 // here the buffer should take care that it is
159 // saved properly, before it goes into the void.
161 // make sure that views using this buffer
166 if (!tmppath.empty()) {
167 DestroyBufferTmpDir(tmppath);
170 Paragraph * par = paragraph;
173 tmppar = par->next();
181 string const Buffer::getLatexName(bool no_path) const
183 string name = ChangeExtension(MakeLatexName(filename), ".tex");
185 return OnlyFilename(name);
191 pair<Buffer::LogType, string> const Buffer::getLogName(void) const
193 string const filename = getLatexName(false);
195 if (filename.empty())
196 return make_pair(Buffer::latexlog, string());
198 string path = OnlyPath(filename);
200 if (lyxrc.use_tempdir || (IsDirWriteable(path) < 1))
203 string const fname = AddName(path,
204 OnlyFilename(ChangeExtension(filename,
207 AddName(path, OnlyFilename(
208 ChangeExtension(filename,
209 formats.Extension("literate") + ".out")));
211 // If no Latex log or Build log is newer, show Build log
213 FileInfo const f_fi(fname);
214 FileInfo const b_fi(bname);
217 (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
218 lyxerr[Debug::FILES] << "Log name calculated as : " << bname << endl;
219 return make_pair(Buffer::buildlog, bname);
221 lyxerr[Debug::FILES] << "Log name calculated as : " << fname << endl;
222 return make_pair(Buffer::latexlog, fname);
226 void Buffer::setReadonly(bool flag)
228 if (read_only != flag) {
231 users->owner()->getDialogs()->updateBufferDependent(false);
234 WarnReadonly(filename);
239 bool Buffer::saveParamsAsDefaults() // const
241 string const fname = AddName(AddPath(user_lyxdir, "templates/"),
243 Buffer defaults = Buffer(fname);
245 // Use the current buffer's parameters as default
246 defaults.params = params;
248 // add an empty paragraph. Is this enough?
249 defaults.paragraph = new Paragraph;
251 return defaults.writeFile(defaults.filename, false);
255 /// Update window titles of all users
256 // Should work on a list
257 void Buffer::updateTitles() const
259 if (users) users->owner()->updateWindowTitle();
263 /// Reset autosave timer of all users
264 // Should work on a list
265 void Buffer::resetAutosaveTimers() const
267 if (users) 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;
287 ErtComp() : active(false), in_tabular(false) {
294 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)
315 ert_comp.contents.erase();
316 ert_comp.active = false;
317 ert_comp.in_tabular = false;
321 Paragraph::depth_type depth = 0;
322 bool the_end_read = false;
324 Paragraph * first_par = 0;
325 LyXFont font(LyXFont::ALL_INHERIT, params.language);
326 if (file_format < 216 && params.language->lang() == "hebrew")
327 font.setLanguage(default_language);
332 // We are inserting into an existing document
333 users->text->breakParagraph(users);
334 first_par = users->text->firstParagraph();
337 // We don't want to adopt the parameters from the
338 // document we insert, so we skip until the text begins:
341 string const pretoken = lex.GetString();
342 if (pretoken == "\\layout") {
343 lex.pushToken(pretoken);
351 string const token = lex.GetString();
353 if (token.empty()) continue;
355 lyxerr[Debug::PARSER] << "Handling token: `"
356 << token << "'" << endl;
359 parseSingleLyXformat2Token(lex, par, first_par,
367 paragraph = first_par;
369 if (unknown_layouts > 0) {
370 string s = _("Couldn't set the layout for ");
371 if (unknown_layouts == 1) {
372 s += _("one paragraph");
374 s += tostr(unknown_layouts);
375 s += _(" paragraphs");
377 WriteAlert(_("Textclass Loading Error!"),s);
384 void Buffer::insertErtContents(Paragraph * par, int & pos,
385 LyXFont const & font, bool set_inactive)
387 if (!ert_comp.contents.empty()) {
388 lyxerr[Debug::INSETS] << "ERT contents:\n"
389 << ert_comp.contents << endl;
390 Inset * inset = new InsetERT(ert_comp.contents);
391 par->insertInset(pos++, inset, font);
392 ert_comp.contents.erase();
395 ert_comp.active = false;
401 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
402 Paragraph *& first_par,
403 string const & token, int & pos,
404 Paragraph::depth_type & depth,
408 bool the_end_read = false;
409 #ifndef NO_PEXTRA_REALLY
410 // This is super temporary but is needed to get the compability
411 // mode for minipages work correctly together with new tabulars.
412 static int call_depth;
414 bool checkminipage = false;
415 static Paragraph * minipar;
416 static Paragraph * parBeforeMinipage;
419 if (token[0] != '\\') {
421 if (ert_comp.active) {
422 ert_comp.contents += token;
425 for (string::const_iterator cit = token.begin();
426 cit != token.end(); ++cit) {
427 par->insertChar(pos, (*cit), font);
430 checkminipage = true;
434 } else if (token == "\\i") {
435 Inset * inset = new InsetLatexAccent;
436 inset->read(this, lex);
437 par->insertInset(pos, inset, font);
439 } else if (token == "\\layout") {
441 ert_comp.in_tabular = false;
443 insertErtContents(par, pos, font);
446 string const layoutname = lex.GetString();
447 pair<bool, LyXTextClass::LayoutList::size_type> pp
448 = textclasslist.NumberOfLayout(params.textclass,
452 if (compare_no_case(layoutname, "latex") == 0) {
453 ert_comp.active = true;
457 // The is the compability reading of layout caption.
458 // It can be removed in LyX version 1.3.0. (Lgb)
459 if (compare_no_case(layoutname, "caption") == 0) {
460 // We expect that the par we are now working on is
461 // really inside a InsetText inside a InsetFloat.
462 // We also know that captions can only be
463 // one paragraph. (Lgb)
465 // We should now read until the next "\layout"
467 // This is probably not good enough, what if the
468 // caption is the last par in the document (Lgb)
469 istream & ist = lex.getStream();
475 if (prefixIs(line, "\\layout")) {
479 if (prefixIs(line, "\\begin_inset"))
481 if (prefixIs(line, "\\end_inset")) {
492 // Now we should have the whole layout in ss
493 // we should now be able to give this to the
495 ss << "\\end_inset\n";
497 // This seems like a bug in stringstream.
498 // We really should be able to use ss
500 istringstream is(ss.str());
502 tmplex.setStream(is);
503 Inset * inset = new InsetCaption;
504 inset->Read(this, tmplex);
505 par->InsertInset(pos, inset, font);
512 par = new Paragraph(par);
516 par->layout = pp.second;
519 // use default layout "Standard" (0)
522 string const s = _("Layout had to be changed from\n")
523 + layoutname + _(" to ")
524 + textclasslist.NameOfLayout(params.textclass, par->layout);
525 InsetError * new_inset = new InsetError(s);
526 par->insertInset(0, new_inset);
528 // Test whether the layout is obsolete.
529 LyXLayout const & layout =
530 textclasslist.Style(params.textclass,
532 if (!layout.obsoleted_by().empty())
533 par->layout = textclasslist
534 .NumberOfLayout(params.textclass,
535 layout.obsoleted_by())
537 par->params().depth(depth);
538 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
539 if (file_format < 216
540 && params.language->lang() == "hebrew")
541 font.setLanguage(default_language);
546 } else if (token == "\\begin_float") {
547 // This is the compability reader. It can be removed in
548 // LyX version 1.3.0. (Lgb)
550 string const tmptok = lex.GetString();
551 //lyxerr << "old float: " << tmptok << endl;
554 stringstream old_float;
556 if (tmptok == "footnote") {
557 inset = new InsetFoot;
558 } else if (tmptok == "margin") {
559 inset = new InsetMarginal;
560 } else if (tmptok == "fig") {
561 inset = new InsetFloat("figure");
562 old_float << "placement htbp\n"
564 } else if (tmptok == "tab") {
565 inset = new InsetFloat("table");
566 old_float << "placement htbp\n"
568 } else if (tmptok == "alg") {
569 inset = new InsetFloat("algorithm");
570 old_float << "placement htbp\n"
572 } else if (tmptok == "wide-fig") {
573 inset = new InsetFloat("figure");
574 //InsetFloat * tmp = new InsetFloat("figure");
577 old_float << "placement htbp\n"
579 } else if (tmptok == "wide-tab") {
580 inset = new InsetFloat("table");
581 //InsetFloat * tmp = new InsetFloat("table");
584 old_float << "placement htbp\n"
590 return false; // no end read yet
593 old_float << "collapsed true\n";
595 // Here we need to check for \end_deeper and handle that
596 // before we do the footnote parsing.
597 // This _is_ a hack! (Lgb)
600 string const tmp = lex.GetString();
601 if (tmp == "\\end_deeper") {
602 //lyxerr << "\\end_deeper caught!" << endl;
604 lex.printError("\\end_deeper: "
605 "depth is already null");
610 old_float << tmp << ' ';
615 old_float << lex.getLongString("\\end_float")
616 << "\n\\end_inset\n";
617 //lyxerr << "Float Body:\n" << old_float.str() << endl;
618 // That this does not work seems like a bug
619 // in stringstream. (Lgb)
620 istringstream istr(old_float.str());
622 nylex.setStream(istr);
623 inset->read(this, nylex);
624 par->insertInset(pos, inset, font);
626 } else if (token == "\\begin_deeper") {
628 } else if (token == "\\end_deeper") {
630 lex.printError("\\end_deeper: "
631 "depth is already null");
635 } else if (token == "\\begin_preamble") {
636 params.readPreamble(lex);
637 } else if (token == "\\textclass") {
639 pair<bool, LyXTextClassList::size_type> pp =
640 textclasslist.NumberOfClass(lex.GetString());
642 params.textclass = pp.second;
644 WriteAlert(string(_("Textclass error")),
645 string(_("The document uses an unknown textclass \"")) +
646 lex.GetString() + string("\"."),
647 string(_("LyX will not be able to produce output correctly.")));
648 params.textclass = 0;
650 if (!textclasslist.Load(params.textclass)) {
651 // if the textclass wasn't loaded properly
652 // we need to either substitute another
653 // or stop loading the file.
654 // I can substitute but I don't see how I can
655 // stop loading... ideas?? ARRae980418
656 WriteAlert(_("Textclass Loading Error!"),
657 string(_("Can't load textclass ")) +
658 textclasslist.NameOfClass(params.textclass),
659 _("-- substituting default"));
660 params.textclass = 0;
662 } else if (token == "\\options") {
664 params.options = lex.GetString();
665 } else if (token == "\\language") {
666 params.readLanguage(lex);
667 } else if (token == "\\fontencoding") {
669 } else if (token == "\\inputencoding") {
671 params.inputenc = lex.GetString();
672 } else if (token == "\\graphics") {
673 params.readGraphicsDriver(lex);
674 } else if (token == "\\fontscheme") {
676 params.fonts = lex.GetString();
677 } else if (token == "\\noindent") {
678 par->params().noindent(true);
679 } else if (token == "\\fill_top") {
680 par->params().spaceTop(VSpace(VSpace::VFILL));
681 } else if (token == "\\fill_bottom") {
682 par->params().spaceBottom(VSpace(VSpace::VFILL));
683 } else if (token == "\\line_top") {
684 par->params().lineTop(true);
685 } else if (token == "\\line_bottom") {
686 par->params().lineBottom(true);
687 } else if (token == "\\pagebreak_top") {
688 par->params().pagebreakTop(true);
689 } else if (token == "\\pagebreak_bottom") {
690 par->params().pagebreakBottom(true);
691 } else if (token == "\\start_of_appendix") {
692 par->params().startOfAppendix(true);
693 } else if (token == "\\paragraph_separation") {
694 int tmpret = lex.FindToken(string_paragraph_separation);
695 if (tmpret == -1) ++tmpret;
696 if (tmpret != LYX_LAYOUT_DEFAULT)
697 params.paragraph_separation =
698 static_cast<BufferParams::PARSEP>(tmpret);
699 } else if (token == "\\defskip") {
701 params.defskip = VSpace(lex.GetString());
702 } else if (token == "\\epsfig") { // obsolete
703 // Indeed it is obsolete, but we HAVE to be backwards
704 // compatible until 0.14, because otherwise all figures
705 // in existing documents are irretrivably lost. (Asger)
706 params.readGraphicsDriver(lex);
707 } else if (token == "\\quotes_language") {
708 int tmpret = lex.FindToken(string_quotes_language);
709 if (tmpret == -1) ++tmpret;
710 if (tmpret != LYX_LAYOUT_DEFAULT) {
711 InsetQuotes::quote_language tmpl =
712 InsetQuotes::EnglishQ;
715 tmpl = InsetQuotes::EnglishQ;
718 tmpl = InsetQuotes::SwedishQ;
721 tmpl = InsetQuotes::GermanQ;
724 tmpl = InsetQuotes::PolishQ;
727 tmpl = InsetQuotes::FrenchQ;
730 tmpl = InsetQuotes::DanishQ;
733 params.quotes_language = tmpl;
735 } else if (token == "\\quotes_times") {
737 switch (lex.GetInteger()) {
739 params.quotes_times = InsetQuotes::SingleQ;
742 params.quotes_times = InsetQuotes::DoubleQ;
745 } else if (token == "\\papersize") {
746 int tmpret = lex.FindToken(string_papersize);
750 params.papersize2 = tmpret;
751 } else if (token == "\\paperpackage") {
752 int tmpret = lex.FindToken(string_paperpackages);
755 params.paperpackage = BufferParams::PACKAGE_NONE;
757 params.paperpackage = tmpret;
758 } else if (token == "\\use_geometry") {
760 params.use_geometry = lex.GetInteger();
761 } else if (token == "\\use_amsmath") {
763 params.use_amsmath = lex.GetInteger();
764 } else if (token == "\\paperorientation") {
765 int tmpret = lex.FindToken(string_orientation);
766 if (tmpret == -1) ++tmpret;
767 if (tmpret != LYX_LAYOUT_DEFAULT)
768 params.orientation = static_cast<BufferParams::PAPER_ORIENTATION>(tmpret);
769 } else if (token == "\\paperwidth") {
771 params.paperwidth = lex.GetString();
772 } else if (token == "\\paperheight") {
774 params.paperheight = lex.GetString();
775 } else if (token == "\\leftmargin") {
777 params.leftmargin = lex.GetString();
778 } else if (token == "\\topmargin") {
780 params.topmargin = lex.GetString();
781 } else if (token == "\\rightmargin") {
783 params.rightmargin = lex.GetString();
784 } else if (token == "\\bottommargin") {
786 params.bottommargin = lex.GetString();
787 } else if (token == "\\headheight") {
789 params.headheight = lex.GetString();
790 } else if (token == "\\headsep") {
792 params.headsep = lex.GetString();
793 } else if (token == "\\footskip") {
795 params.footskip = lex.GetString();
796 } else if (token == "\\paperfontsize") {
798 params.fontsize = strip(lex.GetString());
799 } else if (token == "\\papercolumns") {
801 params.columns = lex.GetInteger();
802 } else if (token == "\\papersides") {
804 switch (lex.GetInteger()) {
806 case 1: params.sides = LyXTextClass::OneSide; break;
807 case 2: params.sides = LyXTextClass::TwoSides; break;
809 } else if (token == "\\paperpagestyle") {
811 params.pagestyle = strip(lex.GetString());
812 } else if (token == "\\bullet") {
814 int const index = lex.GetInteger();
816 int temp_int = lex.GetInteger();
817 params.user_defined_bullets[index].setFont(temp_int);
818 params.temp_bullets[index].setFont(temp_int);
820 temp_int = lex.GetInteger();
821 params.user_defined_bullets[index].setCharacter(temp_int);
822 params.temp_bullets[index].setCharacter(temp_int);
824 temp_int = lex.GetInteger();
825 params.user_defined_bullets[index].setSize(temp_int);
826 params.temp_bullets[index].setSize(temp_int);
828 string const temp_str = lex.GetString();
829 if (temp_str != "\\end_bullet") {
830 // this element isn't really necessary for
831 // parsing but is easier for humans
832 // to understand bullets. Put it back and
833 // set a debug message?
834 lex.printError("\\end_bullet expected, got" + temp_str);
835 //how can I put it back?
837 } else if (token == "\\bulletLaTeX") {
839 int const index = lex.GetInteger();
841 string temp_str = lex.GetString();
843 while (temp_str != "\\end_bullet") {
844 // this loop structure is needed when user
845 // enters an empty string since the first
846 // thing returned will be the \\end_bullet
848 // if the LaTeX entry has spaces. Each element
849 // therefore needs to be read in turn
852 temp_str = lex.GetString();
854 params.user_defined_bullets[index].setText(sum_str);
855 params.temp_bullets[index].setText(sum_str);
856 } else if (token == "\\secnumdepth") {
858 params.secnumdepth = lex.GetInteger();
859 } else if (token == "\\tocdepth") {
861 params.tocdepth = lex.GetInteger();
862 } else if (token == "\\spacing") {
864 string const tmp = strip(lex.GetString());
865 Spacing::Space tmp_space = Spacing::Default;
867 if (tmp == "single") {
868 tmp_space = Spacing::Single;
869 } else if (tmp == "onehalf") {
870 tmp_space = Spacing::Onehalf;
871 } else if (tmp == "double") {
872 tmp_space = Spacing::Double;
873 } else if (tmp == "other") {
875 tmp_space = Spacing::Other;
876 tmp_val = lex.GetFloat();
878 lex.printError("Unknown spacing token: '$$Token'");
880 // Small hack so that files written with klyx will be
883 par->params().spacing(Spacing(tmp_space, tmp_val));
885 params.spacing.set(tmp_space, tmp_val);
887 } else if (token == "\\paragraph_spacing") {
889 string const tmp = strip(lex.GetString());
890 if (tmp == "single") {
891 par->params().spacing(Spacing(Spacing::Single));
892 } else if (tmp == "onehalf") {
893 par->params().spacing(Spacing(Spacing::Onehalf));
894 } else if (tmp == "double") {
895 par->params().spacing(Spacing(Spacing::Double));
896 } else if (tmp == "other") {
898 par->params().spacing(Spacing(Spacing::Other,
901 lex.printError("Unknown spacing token: '$$Token'");
903 } else if (token == "\\float_placement") {
905 params.float_placement = lex.GetString();
906 } else if (token == "\\family") {
908 font.setLyXFamily(lex.GetString());
909 } else if (token == "\\series") {
911 font.setLyXSeries(lex.GetString());
912 } else if (token == "\\shape") {
914 font.setLyXShape(lex.GetString());
915 } else if (token == "\\size") {
917 font.setLyXSize(lex.GetString());
920 #warning compatability hack needed
922 } else if (token == "\\latex") {
924 string const tok = lex.GetString();
925 // This is dirty, but gone with LyX3. (Asger)
926 if (tok == "no_latex")
927 font.setLatex(LyXFont::OFF);
928 else if (tok == "latex")
929 font.setLatex(LyXFont::ON);
930 else if (tok == "default")
931 font.setLatex(LyXFont::INHERIT);
933 lex.printError("Unknown LaTeX font flag "
936 } else if (token == "\\latex") {
938 string const tok = lex.GetString();
939 if (tok == "no_latex") {
941 insertErtContents(par, pos, font);
942 } else if (tok == "latex") {
943 ert_comp.active = true;
944 } else if (tok == "default") {
946 insertErtContents(par, pos, font);
948 lex.printError("Unknown LaTeX font flag "
952 } else if (token == "\\lang") {
954 string const tok = lex.GetString();
955 Language const * lang = languages.getLanguage(tok);
957 font.setLanguage(lang);
959 font.setLanguage(params.language);
960 lex.printError("Unknown language `$$Token'");
962 } else if (token == "\\numeric") {
964 font.setNumber(font.setLyXMisc(lex.GetString()));
965 } else if (token == "\\emph") {
967 font.setEmph(font.setLyXMisc(lex.GetString()));
968 } else if (token == "\\bar") {
970 string const tok = lex.GetString();
971 // This is dirty, but gone with LyX3. (Asger)
973 font.setUnderbar(LyXFont::ON);
974 else if (tok == "no")
975 font.setUnderbar(LyXFont::OFF);
976 else if (tok == "default")
977 font.setUnderbar(LyXFont::INHERIT);
979 lex.printError("Unknown bar font flag "
981 } else if (token == "\\noun") {
983 font.setNoun(font.setLyXMisc(lex.GetString()));
984 } else if (token == "\\color") {
986 font.setLyXColor(lex.GetString());
987 } else if (token == "\\align") {
988 int tmpret = lex.FindToken(string_align);
989 if (tmpret == -1) ++tmpret;
990 if (tmpret != LYX_LAYOUT_DEFAULT) { // tmpret != 99 ???
991 int const tmpret2 = int(pow(2.0, tmpret));
992 //lyxerr << "Tmpret2 = " << tmpret2 << endl;
993 par->params().align(LyXAlignment(tmpret2));
995 } else if (token == "\\added_space_top") {
997 par->params().spaceTop(VSpace(lex.GetString()));
998 } else if (token == "\\added_space_bottom") {
1000 par->params().spaceBottom(VSpace(lex.GetString()));
1001 #ifndef NO_PEXTRA_REALLY
1002 } else if (token == "\\pextra_type") {
1004 par->params().pextraType(lex.GetInteger());
1005 } else if (token == "\\pextra_width") {
1007 par->params().pextraWidth(lex.GetString());
1008 } else if (token == "\\pextra_widthp") {
1010 par->params().pextraWidthp(lex.GetString());
1011 } else if (token == "\\pextra_alignment") {
1013 par->params().pextraAlignment(lex.GetInteger());
1014 } else if (token == "\\pextra_hfill") {
1016 par->params().pextraHfill(lex.GetInteger());
1017 } else if (token == "\\pextra_start_minipage") {
1019 par->params().pextraStartMinipage(lex.GetInteger());
1021 } else if (token == "\\labelwidthstring") {
1023 par->params().labelWidthString(lex.GetString());
1024 // do not delete this token, it is still needed!
1025 } else if (token == "\\end_inset") {
1026 lyxerr << "Solitary \\end_inset. Missing \\begin_inset?.\n"
1027 << "Last inset read was: " << last_inset_read
1029 // Simply ignore this. The insets do not have
1031 // But insets should read it, it is a part of
1032 // the inset isn't it? Lgb.
1033 } else if (token == "\\begin_inset") {
1035 insertErtContents(par, pos, font, false);
1036 ert_stack.push(ert_comp);
1037 ert_comp = ErtComp();
1039 readInset(lex, par, pos, font);
1041 ert_comp = ert_stack.top();
1044 } else if (token == "\\SpecialChar") {
1045 LyXLayout const & layout =
1046 textclasslist.Style(params.textclass,
1049 // Insets don't make sense in a free-spacing context! ---Kayvan
1050 if (layout.free_spacing) {
1053 string next_token = lex.GetString();
1054 if (next_token == "\\-") {
1055 par->insertChar(pos, '-', font);
1056 } else if (next_token == "\\protected_separator"
1057 || next_token == "~") {
1058 par->insertChar(pos, ' ', font);
1060 lex.printError("Token `$$Token' "
1062 "paragraph layout!");
1067 Inset * inset = new InsetSpecialChar;
1068 inset->read(this, lex);
1069 par->insertInset(pos, inset, font);
1072 } else if (token == "\\newline") {
1075 if (!ert_comp.in_tabular && ert_comp.active) {
1076 ert_comp.contents += char(Paragraph::META_NEWLINE);
1078 // Since we cannot know it this is only a regular
1079 // newline or a tabular cell delimter we have to
1080 // handle the ERT here.
1081 insertErtContents(par, pos, font, false);
1083 par->insertChar(pos, Paragraph::META_NEWLINE, font);
1087 par->insertChar(pos, Paragraph::META_NEWLINE, font);
1090 } else if (token == "\\LyXTable") {
1092 ert_comp.in_tabular = true;
1094 Inset * inset = new InsetTabular(*this);
1095 inset->read(this, lex);
1096 par->insertInset(pos, inset, font);
1098 // because of OLD_TABULAR_READ where tabulars have been
1100 checkminipage = true;
1101 } else if (token == "\\hfill") {
1102 par->insertChar(pos, Paragraph::META_HFILL, font);
1104 } else if (token == "\\protected_separator") { // obsolete
1105 // This is a backward compability thingie. (Lgb)
1106 // Remove it later some time...introduced with fileformat
1108 LyXLayout const & layout =
1109 textclasslist.Style(params.textclass,
1112 if (layout.free_spacing) {
1113 par->insertChar(pos, ' ', font);
1115 Inset * inset = new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
1116 par->insertInset(pos, inset, font);
1119 } else if (token == "\\bibitem") { // ale970302
1121 InsetCommandParams p("bibitem", "dummy");
1122 par->bibkey = new InsetBibKey(p);
1124 par->bibkey->read(this, lex);
1125 } else if (token == "\\backslash") {
1127 if (ert_comp.active) {
1128 ert_comp.contents += "\\";
1131 par->insertChar(pos, '\\', font);
1136 } else if (token == "\\the_end") {
1138 // If we still have some ert active here we have to insert
1139 // it so we don't loose it. (Lgb)
1140 insertErtContents(par, pos, font);
1142 the_end_read = true;
1143 minipar = parBeforeMinipage = 0;
1146 if (ert_comp.active) {
1147 ert_comp.contents += token;
1150 // This should be insurance for the future: (Asger)
1151 lex.printError("Unknown token `$$Token'. "
1152 "Inserting as text.");
1153 string::const_iterator cit = token.begin();
1154 string::const_iterator end = token.end();
1155 for (; cit != end; ++cit) {
1156 par->insertChar(pos, (*cit), font);
1163 #ifndef NO_PEXTRA_REALLY
1164 // now check if we have a minipage paragraph as at this
1165 // point we already read all the necessary data!
1166 // this cannot be done in layout because there we did
1167 // not read yet the paragraph PEXTRA-params (Jug)
1169 // BEGIN pextra_minipage compability
1170 // This should be removed in 1.3.x (Lgb)
1172 // This compability code is not perfect. In a couple
1173 // of rand cases it fails. When the minipage par is
1174 // the first par in the document, and when there are
1175 // none or only one regular paragraphs after the
1176 // minipage. Currently I am not investing any effort
1177 // in fixing those cases.
1179 //lyxerr << "Call depth: " << call_depth << endl;
1180 if (checkminipage && (call_depth == 1)) {
1181 checkminipage = false;
1182 if (minipar && (minipar != par) &&
1183 (par->params().pextraType()==Paragraph::PEXTRA_MINIPAGE))
1185 lyxerr << "minipages in a row" << endl;
1186 if (par->params().pextraStartMinipage()) {
1187 lyxerr << "start new minipage" << endl;
1188 // minipages in a row
1189 par->previous()->next(0);
1192 Paragraph * tmp = minipar;
1194 tmp->params().pextraType(0);
1195 tmp->params().pextraWidth(string());
1196 tmp->params().pextraWidthp(string());
1197 tmp->params().pextraAlignment(0);
1198 tmp->params().pextraHfill(false);
1199 tmp->params().pextraStartMinipage(false);
1202 // create a new paragraph to insert the
1203 // minipages in the following case
1204 if (par->params().pextraStartMinipage() &&
1205 !par->params().pextraHfill())
1207 Paragraph * p = new Paragraph;
1209 p->previous(parBeforeMinipage);
1210 parBeforeMinipage->next(p);
1212 p->params().depth(parBeforeMinipage->params().depth());
1213 parBeforeMinipage = p;
1215 InsetMinipage * mini = new InsetMinipage;
1216 mini->pos(static_cast<InsetMinipage::Position>(par->params().pextraAlignment()));
1217 mini->width(par->params().pextraWidth());
1218 if (!par->params().pextraWidthp().empty()) {
1219 lyxerr << "WP:" << mini->width() << endl;
1220 mini->width(tostr(par->params().pextraWidthp())+"%");
1222 mini->inset.paragraph(par);
1223 // Insert the minipage last in the
1224 // previous paragraph.
1225 if (par->params().pextraHfill()) {
1226 parBeforeMinipage->insertChar
1227 (parBeforeMinipage->size(), Paragraph::META_HFILL);
1229 parBeforeMinipage->insertInset
1230 (parBeforeMinipage->size(), mini);
1234 lyxerr << "new minipage par" << endl;
1235 //nothing to do just continue reading
1238 } else if (minipar && (minipar != par)) {
1239 lyxerr << "last minipage par read" << endl;
1240 // The last paragraph read was not part of a
1241 // minipage but the par linked list is...
1242 // So we need to remove the last par from the
1244 if (par->previous())
1245 par->previous()->next(0);
1246 par->previous(parBeforeMinipage);
1247 parBeforeMinipage->next(par);
1248 Paragraph * tmp = minipar;
1250 tmp->params().pextraType(0);
1251 tmp->params().pextraWidth(string());
1252 tmp->params().pextraWidthp(string());
1253 tmp->params().pextraAlignment(0);
1254 tmp->params().pextraHfill(false);
1255 tmp->params().pextraStartMinipage(false);
1258 depth = parBeforeMinipage->params().depth();
1259 minipar = parBeforeMinipage = 0;
1260 } else if (!minipar &&
1261 (par->params().pextraType() == Paragraph::PEXTRA_MINIPAGE))
1263 // par is the first paragraph in a minipage
1264 lyxerr << "begin minipage" << endl;
1265 // To minimize problems for
1266 // the users we will insert
1267 // the first minipage in
1268 // a sequence of minipages
1269 // in its own paragraph.
1270 Paragraph * p = new Paragraph;
1272 p->previous(par->previous());
1274 p->params().depth(depth);
1275 par->params().depth(0);
1277 if (par->previous())
1278 par->previous()->next(p);
1280 parBeforeMinipage = p;
1282 if (!first_par || (first_par == par))
1285 InsetMinipage * mini = new InsetMinipage;
1286 mini->pos(static_cast<InsetMinipage::Position>(minipar->params().pextraAlignment()));
1287 mini->width(minipar->params().pextraWidth());
1288 if (!par->params().pextraWidthp().empty()) {
1289 lyxerr << "WP:" << mini->width() << endl;
1290 mini->width(tostr(par->params().pextraWidthp())+"%");
1292 mini->inset.paragraph(minipar);
1294 // Insert the minipage last in the
1295 // previous paragraph.
1296 if (minipar->params().pextraHfill()) {
1297 parBeforeMinipage->insertChar
1298 (parBeforeMinipage->size(),Paragraph::META_HFILL);
1300 parBeforeMinipage->insertInset
1301 (parBeforeMinipage->size(), mini);
1304 // End of pextra_minipage compability
1307 return the_end_read;
1311 void Buffer::readInset(LyXLex & lex, Paragraph *& par,
1312 int & pos, LyXFont & font)
1314 // consistency check
1315 if (lex.GetString() != "\\begin_inset") {
1316 lyxerr << "Buffer::readInset: Consistency check failed."
1323 string const tmptok = lex.GetString();
1324 last_inset_read = tmptok;
1326 // test the different insets
1327 if (tmptok == "LatexCommand") {
1328 InsetCommandParams inscmd;
1331 string const cmdName = inscmd.getCmdName();
1333 if (cmdName == "cite") {
1334 inset = new InsetCitation(inscmd);
1335 } else if (cmdName == "bibitem") {
1336 lex.printError("Wrong place for bibitem");
1337 inset = new InsetBibKey(inscmd);
1338 } else if (cmdName == "BibTeX") {
1339 inset = new InsetBibtex(inscmd);
1340 } else if (cmdName == "index") {
1341 inset = new InsetIndex(inscmd);
1342 } else if (cmdName == "include") {
1343 inset = new InsetInclude(inscmd, *this);
1344 } else if (cmdName == "label") {
1345 inset = new InsetLabel(inscmd);
1346 } else if (cmdName == "url"
1347 || cmdName == "htmlurl") {
1348 inset = new InsetUrl(inscmd);
1349 } else if (cmdName == "ref"
1350 || cmdName == "pageref"
1351 || cmdName == "vref"
1352 || cmdName == "vpageref"
1353 || cmdName == "prettyref") {
1354 if (!inscmd.getOptions().empty()
1355 || !inscmd.getContents().empty()) {
1356 inset = new InsetRef(inscmd, *this);
1358 } else if (cmdName == "tableofcontents") {
1359 inset = new InsetTOC(inscmd);
1360 } else if (cmdName == "listofalgorithms") {
1361 inset = new InsetFloatList("algorithm");
1362 } else if (cmdName == "listoffigures") {
1363 inset = new InsetFloatList("figure");
1364 } else if (cmdName == "listoftables") {
1365 inset = new InsetFloatList("table");
1366 } else if (cmdName == "printindex") {
1367 inset = new InsetPrintIndex(inscmd);
1368 } else if (cmdName == "lyxparent") {
1369 inset = new InsetParent(inscmd, *this);
1372 if (tmptok == "Quotes") {
1373 inset = new InsetQuotes;
1374 } else if (tmptok == "External") {
1375 inset = new InsetExternal;
1376 } else if (tmptok == "FormulaMacro") {
1377 inset = new InsetFormulaMacro;
1378 } else if (tmptok == "Formula") {
1379 inset = new InsetFormula;
1380 } else if (tmptok == "Figure") {
1381 inset = new InsetFig(100, 100, *this);
1382 } else if (tmptok == "Info") {
1383 inset = new InsetInfo;
1384 } else if (tmptok == "Include") {
1385 InsetCommandParams p( "Include" );
1386 inset = new InsetInclude(p, *this);
1387 } else if (tmptok == "ERT") {
1388 inset = new InsetERT;
1389 } else if (tmptok == "Tabular") {
1390 inset = new InsetTabular(*this);
1391 } else if (tmptok == "Text") {
1392 inset = new InsetText;
1393 } else if (tmptok == "Foot") {
1394 inset = new InsetFoot;
1395 } else if (tmptok == "Marginal") {
1396 inset = new InsetMarginal;
1397 } else if (tmptok == "Minipage") {
1398 inset = new InsetMinipage;
1399 } else if (tmptok == "Float") {
1401 string tmptok = lex.GetString();
1402 inset = new InsetFloat(tmptok);
1403 } else if (tmptok == "List") {
1404 inset = new InsetList;
1405 } else if (tmptok == "Theorem") {
1406 inset = new InsetList;
1407 } else if (tmptok == "Caption") {
1408 inset = new InsetCaption;
1409 } else if (tmptok == "GRAPHICS") {
1410 inset = new InsetGraphics;
1411 } else if (tmptok == "FloatList") {
1412 inset = new InsetFloatList;
1415 if (inset) inset->read(this, lex);
1419 par->insertInset(pos, inset, font);
1425 bool Buffer::readFile(LyXLex & lex, Paragraph * par)
1429 string const token(lex.GetString());
1430 if (token == "\\lyxformat") { // the first token _must_ be...
1432 string tmp_format = lex.GetString();
1433 //lyxerr << "LyX Format: `" << tmp_format << "'" << endl;
1434 // if present remove ".," from string.
1435 string::size_type dot = tmp_format.find_first_of(".,");
1436 //lyxerr << " dot found at " << dot << endl;
1437 if (dot != string::npos)
1438 tmp_format.erase(dot, 1);
1439 file_format = strToInt(tmp_format);
1440 if (file_format == LYX_FORMAT) {
1442 } else if (file_format > LYX_FORMAT) {
1444 WriteAlert(_("Warning!"),
1445 _("LyX file format is newer that what"),
1446 _("is supported in this LyX version. Expect some problems."));
1448 } else if (file_format < LYX_FORMAT) {
1450 if (file_format < 200) {
1451 WriteAlert(_("ERROR!"),
1452 _("Old LyX file format found. "
1453 "Use LyX 0.10.x to read this!"));
1457 bool the_end = readLyXformat2(lex, par);
1459 // the_end was added in 213
1460 if (file_format < 213)
1464 WriteAlert(_("Warning!"),
1465 _("Reading of document is not complete"),
1466 _("Maybe the document is truncated"));
1468 } else { // "\\lyxformat" not found
1469 WriteAlert(_("ERROR!"), _("Not a LyX file!"));
1472 WriteAlert(_("ERROR!"), _("Unable to read file!"));
1477 // Should probably be moved to somewhere else: BufferView? LyXView?
1478 bool Buffer::save() const
1480 // We don't need autosaves in the immediate future. (Asger)
1481 resetAutosaveTimers();
1485 if (lyxrc.make_backup) {
1486 s = fileName() + '~';
1487 if (!lyxrc.backupdir_path.empty())
1488 s = AddName(lyxrc.backupdir_path,
1489 subst(os::slashify_path(s),'/','!'));
1491 // Rename is the wrong way of making a backup,
1492 // this is the correct way.
1493 /* truss cp fil fil2:
1494 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
1495 stat("LyXVC.lyx", 0xEFFFF688) = 0
1496 open("LyXVC.lyx", O_RDONLY) = 3
1497 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
1498 fstat(4, 0xEFFFF508) = 0
1499 fstat(3, 0xEFFFF508) = 0
1500 read(3, " # T h i s f i l e w".., 8192) = 5579
1501 write(4, " # T h i s f i l e w".., 5579) = 5579
1502 read(3, 0xEFFFD4A0, 8192) = 0
1505 chmod("LyXVC3.lyx", 0100644) = 0
1506 lseek(0, 0, SEEK_CUR) = 46440
1510 // Should probably have some more error checking here.
1511 // Should be cleaned up in 0.13, at least a bit.
1512 // Doing it this way, also makes the inodes stay the same.
1513 // This is still not a very good solution, in particular we
1514 // might loose the owner of the backup.
1515 FileInfo finfo(fileName());
1516 if (finfo.exist()) {
1517 mode_t fmode = finfo.getMode();
1518 struct utimbuf times = {
1519 finfo.getAccessTime(),
1520 finfo.getModificationTime() };
1522 ifstream ifs(fileName().c_str());
1523 ofstream ofs(s.c_str(), ios::out|ios::trunc);
1528 ::chmod(s.c_str(), fmode);
1530 if (::utime(s.c_str(), ×)) {
1531 lyxerr << "utime error." << endl;
1534 lyxerr << "LyX was not able to make "
1535 "backup copy. Beware." << endl;
1540 if (writeFile(fileName(), false)) {
1542 removeAutosaveFile(fileName());
1544 // Saving failed, so backup is not backup
1545 if (lyxrc.make_backup) {
1546 lyx::rename(s, fileName());
1554 // Returns false if unsuccesful
1555 bool Buffer::writeFile(string const & fname, bool flag) const
1557 // if flag is false writeFile will not create any GUI
1558 // warnings, only cerr.
1559 // Needed for autosave in background or panic save (Matthias 120496)
1561 if (read_only && (fname == filename)) {
1562 // Here we should come with a question if we should
1563 // perform the write anyway.
1565 lyxerr << _("Error! Document is read-only: ")
1568 WriteAlert(_("Error! Document is read-only: "),
1573 FileInfo finfo(fname);
1574 if (finfo.exist() && !finfo.writable()) {
1575 // Here we should come with a question if we should
1576 // try to do the save anyway. (i.e. do a chmod first)
1578 lyxerr << _("Error! Cannot write file: ")
1581 WriteFSAlert(_("Error! Cannot write file: "),
1586 ofstream ofs(fname.c_str());
1589 lyxerr << _("Error! Cannot open file: ")
1592 WriteFSAlert(_("Error! Cannot open file: "),
1598 // Use the standard "C" locale for file output.
1599 ofs.imbue(std::locale::classic());
1602 // The top of the file should not be written by params.
1604 // write out a comment in the top of the file
1605 ofs << '#' << LYX_DOCVERSION
1606 << " created this file. For more info see http://www.lyx.org/\n"
1607 << "\\lyxformat " << LYX_FORMAT << "\n";
1609 // now write out the buffer paramters.
1610 params.writeFile(ofs);
1612 Paragraph::depth_type depth = 0;
1614 // this will write out all the paragraphs
1615 // using recursive descent.
1616 paragraph->writeFile(this, ofs, params, depth);
1618 // Write marker that shows file is complete
1619 ofs << "\n\\the_end" << endl;
1623 // how to check if close went ok?
1624 // Following is an attempt... (BE 20001011)
1626 // good() returns false if any error occured, including some
1627 // formatting error.
1628 // bad() returns true if something bad happened in the buffer,
1629 // which should include file system full errors.
1636 lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1638 lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1648 string const Buffer::asciiParagraph(Paragraph const * par,
1649 unsigned int linelen) const
1651 ostringstream buffer;
1652 Paragraph::depth_type depth = 0;
1654 Paragraph::depth_type ltype_depth = 0;
1655 unsigned int currlinelen = 0;
1656 bool ref_printed = false;
1660 if (!par->previous()) {
1661 // begins or ends a deeper area ?
1662 if (depth != par->params().depth()) {
1663 if (par->params().depth() > depth) {
1664 while (par->params().depth() > depth) {
1668 while (par->params().depth() < depth) {
1674 // First write the layout
1675 string const tmp = textclasslist.NameOfLayout(params.textclass, par->layout);
1676 if (tmp == "Itemize") {
1678 ltype_depth = depth + 1;
1679 } else if (tmp == "Enumerate") {
1681 ltype_depth = depth + 1;
1682 } else if (contains(tmp, "ection")) {
1684 ltype_depth = depth + 1;
1685 } else if (contains(tmp, "aragraph")) {
1687 ltype_depth = depth + 1;
1688 } else if (tmp == "Description") {
1690 ltype_depth = depth + 1;
1691 } else if (tmp == "Abstract") {
1694 } else if (tmp == "Bibliography") {
1702 /* maybe some vertical spaces */
1704 /* the labelwidthstring used in lists */
1708 /* some pagebreaks? */
1712 /* what about the alignment */
1714 lyxerr << "Should this ever happen?" << endl;
1718 LyXFont const font1 = LyXFont(LyXFont::ALL_INHERIT, params.language);
1720 for (Paragraph::size_type i = 0; i < par->size(); ++i) {
1721 if (!i && !noparbreak) {
1724 for (Paragraph::depth_type j = 0; j < depth; ++j)
1726 currlinelen = depth * 2;
1729 case 4: // (Sub)Paragraph
1730 case 5: // Description
1734 buffer << "Abstract\n\n";
1736 buffer << "Abstract: ";
1738 case 7: // Bibliography
1741 buffer << "References\n\n";
1743 buffer << "References: ";
1748 buffer << par->params().labelString() << " ";
1751 if (ltype_depth > depth) {
1752 for (Paragraph::depth_type j = ltype_depth - 1;
1755 currlinelen += (ltype_depth-depth)*2;
1759 LyXFont const font2 = par->getFontSettings(params, i);
1760 if (font1.latex() != font2.latex()) {
1761 if (font2.latex() == LyXFont::OFF)
1770 char c = par->getUChar(params, i);
1774 case Paragraph::META_INSET:
1776 Inset const * inset = par->getInset(i);
1778 if (!inset->ascii(this, buffer)) {
1781 rsplit(buffer.str().c_str(),
1783 currlinelen += s.length();
1785 // to be sure it breaks paragraph
1786 currlinelen += linelen;
1792 case Paragraph::META_NEWLINE:
1795 for (Paragraph::depth_type j = 0;
1799 currlinelen = depth * 2;
1800 if (ltype_depth > depth) {
1801 for (Paragraph::depth_type j = ltype_depth;
1804 currlinelen += (ltype_depth - depth) * 2;
1808 case Paragraph::META_HFILL:
1817 if ((linelen > 0) && (currlinelen > (linelen - 10)) &&
1818 (c == ' ') && ((i + 2) < par->size()))
1821 for (Paragraph::depth_type j = 0;
1824 currlinelen = depth * 2;
1825 if (ltype_depth > depth) {
1826 for (Paragraph::depth_type j = ltype_depth;
1829 currlinelen += (ltype_depth-depth)*2;
1831 } else if (c != '\0')
1834 lyxerr[Debug::INFO] << "writeAsciiFile: NULL char in structure." << endl;
1839 return buffer.str().c_str();
1843 void Buffer::writeFileAscii(string const & fname, int linelen)
1845 ofstream ofs(fname.c_str());
1847 WriteFSAlert(_("Error: Cannot write file:"), fname);
1850 writeFileAscii(ofs, linelen);
1854 void Buffer::writeFileAscii(ostream & ofs, int linelen)
1856 Paragraph * par = paragraph;
1858 ofs << asciiParagraph(par, linelen);
1866 void Buffer::makeLaTeXFile(string const & fname,
1867 string const & original_path,
1868 bool nice, bool only_body)
1870 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
1872 niceFile = nice; // this will be used by Insetincludes.
1874 tex_code_break_column = lyxrc.ascii_linelen;
1876 LyXTextClass const & tclass =
1877 textclasslist.TextClass(params.textclass);
1879 ofstream ofs(fname.c_str());
1881 WriteFSAlert(_("Error: Cannot open file: "), fname);
1885 // validate the buffer.
1886 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
1887 LaTeXFeatures features(params, tclass.numLayouts());
1889 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
1892 // The starting paragraph of the coming rows is the
1893 // first paragraph of the document. (Asger)
1894 texrow.start(paragraph, 0);
1896 if (!only_body && nice) {
1897 ofs << "%% " LYX_DOCVERSION " created this file. "
1898 "For more info, see http://www.lyx.org/.\n"
1899 "%% Do not edit unless you really know what "
1904 lyxerr[Debug::INFO] << "lyx header finished" << endl;
1905 // There are a few differences between nice LaTeX and usual files:
1906 // usual is \batchmode and has a
1907 // special input@path to allow the including of figures
1908 // with either \input or \includegraphics (what figinsets do).
1909 // batchmode is not set if there is a tex_code_break_column.
1910 // In this case somebody is interested in the generated LaTeX,
1911 // so this is OK. input@path is set when the actual parameter
1912 // original_path is set. This is done for usual tex-file, but not
1913 // for nice-latex-file. (Matthias 250696)
1916 // code for usual, NOT nice-latex-file
1917 ofs << "\\batchmode\n"; // changed
1918 // from \nonstopmode
1921 if (!original_path.empty()) {
1922 ofs << "\\makeatletter\n"
1923 << "\\def\\input@path{{"
1924 << os::external_path(original_path) << "/}}\n"
1925 << "\\makeatother\n";
1931 ofs << "\\documentclass";
1933 ostringstream options; // the document class options.
1935 if (tokenPos(tclass.opt_fontsize(),
1936 '|', params.fontsize) >= 0) {
1937 // only write if existing in list (and not default)
1938 options << params.fontsize << "pt,";
1942 if (!params.use_geometry &&
1943 (params.paperpackage == BufferParams::PACKAGE_NONE)) {
1944 switch (params.papersize) {
1945 case BufferParams::PAPER_A4PAPER:
1946 options << "a4paper,";
1948 case BufferParams::PAPER_USLETTER:
1949 options << "letterpaper,";
1951 case BufferParams::PAPER_A5PAPER:
1952 options << "a5paper,";
1954 case BufferParams::PAPER_B5PAPER:
1955 options << "b5paper,";
1957 case BufferParams::PAPER_EXECUTIVEPAPER:
1958 options << "executivepaper,";
1960 case BufferParams::PAPER_LEGALPAPER:
1961 options << "legalpaper,";
1967 if (params.sides != tclass.sides()) {
1968 switch (params.sides) {
1969 case LyXTextClass::OneSide:
1970 options << "oneside,";
1972 case LyXTextClass::TwoSides:
1973 options << "twoside,";
1979 if (params.columns != tclass.columns()) {
1980 if (params.columns == 2)
1981 options << "twocolumn,";
1983 options << "onecolumn,";
1986 if (!params.use_geometry
1987 && params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
1988 options << "landscape,";
1990 // language should be a parameter to \documentclass
1992 ostringstream language_options;
1993 if (params.language->babel() == "hebrew"
1994 && default_language->babel() != "hebrew")
1995 // This seems necessary
1996 features.UsedLanguages.insert(default_language);
1998 if (lyxrc.language_use_babel ||
1999 params.language->lang() != lyxrc.default_language ||
2000 !features.UsedLanguages.empty()) {
2002 for (LaTeXFeatures::LanguageList::const_iterator cit =
2003 features.UsedLanguages.begin();
2004 cit != features.UsedLanguages.end(); ++cit)
2005 language_options << (*cit)->babel() << ',';
2006 language_options << params.language->babel();
2007 if (lyxrc.language_global_options)
2008 options << language_options.str() << ',';
2011 // the user-defined options
2012 if (!params.options.empty()) {
2013 options << params.options << ',';
2016 string strOptions(options.str().c_str());
2017 if (!strOptions.empty()){
2018 strOptions = strip(strOptions, ',');
2019 ofs << '[' << strOptions << ']';
2023 << textclasslist.LatexnameOfClass(params.textclass)
2026 // end of \documentclass defs
2028 // font selection must be done before loading fontenc.sty
2029 // The ae package is not needed when using OT1 font encoding.
2030 if (params.fonts != "default" &&
2031 (params.fonts != "ae" || lyxrc.fontenc != "default")) {
2032 ofs << "\\usepackage{" << params.fonts << "}\n";
2034 if (params.fonts == "ae") {
2035 ofs << "\\usepackage{aecompl}\n";
2039 // this one is not per buffer
2040 if (lyxrc.fontenc != "default") {
2041 ofs << "\\usepackage[" << lyxrc.fontenc
2046 if (params.inputenc == "auto") {
2047 string const doc_encoding =
2048 params.language->encoding()->LatexName();
2050 // Create a list with all the input encodings used
2052 set<string> encodings;
2053 for (LaTeXFeatures::LanguageList::const_iterator it =
2054 features.UsedLanguages.begin();
2055 it != features.UsedLanguages.end(); ++it)
2056 if ((*it)->encoding()->LatexName() != doc_encoding)
2057 encodings.insert((*it)->encoding()->LatexName());
2059 ofs << "\\usepackage[";
2060 std::copy(encodings.begin(), encodings.end(),
2061 std::ostream_iterator<string>(ofs, ","));
2062 ofs << doc_encoding << "]{inputenc}\n";
2064 } else if (params.inputenc != "default") {
2065 ofs << "\\usepackage[" << params.inputenc
2070 // At the very beginning the text parameters.
2071 if (params.paperpackage != BufferParams::PACKAGE_NONE) {
2072 switch (params.paperpackage) {
2073 case BufferParams::PACKAGE_A4:
2074 ofs << "\\usepackage{a4}\n";
2077 case BufferParams::PACKAGE_A4WIDE:
2078 ofs << "\\usepackage{a4wide}\n";
2081 case BufferParams::PACKAGE_WIDEMARGINSA4:
2082 ofs << "\\usepackage[widemargins]{a4}\n";
2087 if (params.use_geometry) {
2088 ofs << "\\usepackage{geometry}\n";
2090 ofs << "\\geometry{verbose";
2091 if (params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
2092 ofs << ",landscape";
2093 switch (params.papersize2) {
2094 case BufferParams::VM_PAPER_CUSTOM:
2095 if (!params.paperwidth.empty())
2096 ofs << ",paperwidth="
2097 << params.paperwidth;
2098 if (!params.paperheight.empty())
2099 ofs << ",paperheight="
2100 << params.paperheight;
2102 case BufferParams::VM_PAPER_USLETTER:
2103 ofs << ",letterpaper";
2105 case BufferParams::VM_PAPER_USLEGAL:
2106 ofs << ",legalpaper";
2108 case BufferParams::VM_PAPER_USEXECUTIVE:
2109 ofs << ",executivepaper";
2111 case BufferParams::VM_PAPER_A3:
2114 case BufferParams::VM_PAPER_A4:
2117 case BufferParams::VM_PAPER_A5:
2120 case BufferParams::VM_PAPER_B3:
2123 case BufferParams::VM_PAPER_B4:
2126 case BufferParams::VM_PAPER_B5:
2130 // default papersize ie BufferParams::VM_PAPER_DEFAULT
2131 switch (lyxrc.default_papersize) {
2132 case BufferParams::PAPER_DEFAULT: // keep compiler happy
2133 case BufferParams::PAPER_USLETTER:
2134 ofs << ",letterpaper";
2136 case BufferParams::PAPER_LEGALPAPER:
2137 ofs << ",legalpaper";
2139 case BufferParams::PAPER_EXECUTIVEPAPER:
2140 ofs << ",executivepaper";
2142 case BufferParams::PAPER_A3PAPER:
2145 case BufferParams::PAPER_A4PAPER:
2148 case BufferParams::PAPER_A5PAPER:
2151 case BufferParams::PAPER_B5PAPER:
2156 if (!params.topmargin.empty())
2157 ofs << ",tmargin=" << params.topmargin;
2158 if (!params.bottommargin.empty())
2159 ofs << ",bmargin=" << params.bottommargin;
2160 if (!params.leftmargin.empty())
2161 ofs << ",lmargin=" << params.leftmargin;
2162 if (!params.rightmargin.empty())
2163 ofs << ",rmargin=" << params.rightmargin;
2164 if (!params.headheight.empty())
2165 ofs << ",headheight=" << params.headheight;
2166 if (!params.headsep.empty())
2167 ofs << ",headsep=" << params.headsep;
2168 if (!params.footskip.empty())
2169 ofs << ",footskip=" << params.footskip;
2173 if (features.amsstyle
2174 && !tclass.provides(LyXTextClass::amsmath)) {
2175 ofs << "\\usepackage{amsmath}\n";
2179 if (tokenPos(tclass.opt_pagestyle(),
2180 '|', params.pagestyle) >= 0) {
2181 if (params.pagestyle == "fancy") {
2182 ofs << "\\usepackage{fancyhdr}\n";
2185 ofs << "\\pagestyle{" << params.pagestyle << "}\n";
2189 // We try to load babel late, in case it interferes
2190 // with other packages.
2192 string tmp = lyxrc.language_package;
2193 if (!lyxrc.language_global_options
2194 && tmp == "\\usepackage{babel}")
2195 tmp = string("\\usepackage[") +
2196 language_options.str().c_str() +
2202 if (params.secnumdepth != tclass.secnumdepth()) {
2203 ofs << "\\setcounter{secnumdepth}{"
2204 << params.secnumdepth
2208 if (params.tocdepth != tclass.tocdepth()) {
2209 ofs << "\\setcounter{tocdepth}{"
2215 if (params.paragraph_separation) {
2216 switch (params.defskip.kind()) {
2217 case VSpace::SMALLSKIP:
2218 ofs << "\\setlength\\parskip{\\smallskipamount}\n";
2220 case VSpace::MEDSKIP:
2221 ofs << "\\setlength\\parskip{\\medskipamount}\n";
2223 case VSpace::BIGSKIP:
2224 ofs << "\\setlength\\parskip{\\bigskipamount}\n";
2226 case VSpace::LENGTH:
2227 ofs << "\\setlength\\parskip{"
2228 << params.defskip.length().asLatexString()
2231 default: // should never happen // Then delete it.
2232 ofs << "\\setlength\\parskip{\\medskipamount}\n";
2237 ofs << "\\setlength\\parindent{0pt}\n";
2241 // Now insert the LyX specific LaTeX commands...
2243 // The optional packages;
2244 string preamble(features.getPackages());
2246 // this might be useful...
2247 preamble += "\n\\makeatletter\n";
2249 // Some macros LyX will need
2250 string tmppreamble(features.getMacros());
2252 if (!tmppreamble.empty()) {
2253 preamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2254 "LyX specific LaTeX commands.\n"
2255 + tmppreamble + '\n';
2258 // the text class specific preamble
2259 tmppreamble = features.getTClassPreamble();
2260 if (!tmppreamble.empty()) {
2261 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2262 "Textclass specific LaTeX commands.\n"
2263 + tmppreamble + '\n';
2266 /* the user-defined preamble */
2267 if (!params.preamble.empty()) {
2268 preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2269 "User specified LaTeX commands.\n"
2270 + params.preamble + '\n';
2273 preamble += "\\makeatother\n";
2275 // Itemize bullet settings need to be last in case the user
2276 // defines their own bullets that use a package included
2277 // in the user-defined preamble -- ARRae
2278 // Actually it has to be done much later than that
2279 // since some packages like frenchb make modifications
2280 // at \begin{document} time -- JMarc
2282 for (int i = 0; i < 4; ++i) {
2283 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
2284 if (bullets_def.empty())
2285 bullets_def="\\AtBeginDocument{\n";
2286 bullets_def += " \\renewcommand{\\labelitemi";
2288 // `i' is one less than the item to modify
2295 bullets_def += "ii";
2301 bullets_def += "}{" +
2302 params.user_defined_bullets[i].getText()
2307 if (!bullets_def.empty())
2308 preamble += bullets_def + "}\n\n";
2310 for (int j = countChar(preamble, '\n'); j-- ;) {
2317 ofs << "\\begin{document}\n";
2320 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
2322 if (!lyxrc.language_auto_begin) {
2323 ofs << subst(lyxrc.language_command_begin, "$$lang",
2324 params.language->babel())
2329 latexParagraphs(ofs, paragraph, 0, texrow);
2331 // add this just in case after all the paragraphs
2335 if (!lyxrc.language_auto_end) {
2336 ofs << subst(lyxrc.language_command_end, "$$lang",
2337 params.language->babel())
2343 ofs << "\\end{document}\n";
2346 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
2348 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
2352 // Just to be sure. (Asger)
2355 // tex_code_break_column's value is used to decide
2356 // if we are in batchmode or not (within mathed_write()
2357 // in math_write.C) so we must set it to a non-zero
2358 // value when we leave otherwise we save incorrect .lyx files.
2359 tex_code_break_column = lyxrc.ascii_linelen;
2363 lyxerr << "File was not closed properly." << endl;
2366 lyxerr[Debug::INFO] << "Finished making latex file." << endl;
2371 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
2373 void Buffer::latexParagraphs(ostream & ofs, Paragraph * par,
2374 Paragraph * endpar, TexRow & texrow) const
2376 bool was_title = false;
2377 bool already_title = false;
2380 while (par != endpar) {
2381 LyXLayout const & layout =
2382 textclasslist.Style(params.textclass,
2385 if (layout.intitle) {
2386 if (already_title) {
2387 lyxerr <<"Error in latexParagraphs: You"
2388 " should not mix title layouts"
2389 " with normal ones." << endl;
2392 } else if (was_title && !already_title) {
2393 ofs << "\\maketitle\n";
2395 already_title = true;
2399 if (layout.isEnvironment()) {
2400 par = par->TeXEnvironment(this, params, ofs, texrow);
2402 par = par->TeXOnePar(this, params, ofs, texrow, false);
2405 // It might be that we only have a title in this document
2406 if (was_title && !already_title) {
2407 ofs << "\\maketitle\n";
2413 bool Buffer::isLatex() const
2415 return textclasslist.TextClass(params.textclass).outputType() == LATEX;
2419 bool Buffer::isLinuxDoc() const
2421 return textclasslist.TextClass(params.textclass).outputType() == LINUXDOC;
2425 bool Buffer::isLiterate() const
2427 return textclasslist.TextClass(params.textclass).outputType() == LITERATE;
2431 bool Buffer::isDocBook() const
2433 return textclasslist.TextClass(params.textclass).outputType() == DOCBOOK;
2437 bool Buffer::isSGML() const
2439 return textclasslist.TextClass(params.textclass).outputType() == LINUXDOC ||
2440 textclasslist.TextClass(params.textclass).outputType() == DOCBOOK;
2444 void Buffer::sgmlOpenTag(ostream & os, Paragraph::depth_type depth,
2445 string const & latexname) const
2447 if (!latexname.empty() && latexname != "!-- --")
2448 os << "<!-- " << depth << " -->" << "<" << latexname << ">";
2449 //os << string(depth, ' ') << "<" << latexname << ">\n";
2453 void Buffer::sgmlCloseTag(ostream & os, Paragraph::depth_type depth,
2454 string const & latexname) const
2456 if (!latexname.empty() && latexname != "!-- --")
2457 os << "<!-- " << depth << " -->" << "</" << latexname << ">\n";
2458 //os << string(depth, ' ') << "</" << latexname << ">\n";
2462 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
2464 ofstream ofs(fname.c_str());
2467 WriteAlert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2471 niceFile = nice; // this will be used by included files.
2473 LyXTextClass const & tclass =
2474 textclasslist.TextClass(params.textclass);
2476 LaTeXFeatures features(params, tclass.numLayouts());
2481 string top_element = textclasslist.LatexnameOfClass(params.textclass);
2484 string sgml_includedfiles=features.getIncludedFiles(fname);
2486 if (params.preamble.empty() && sgml_includedfiles.empty()) {
2487 ofs << "<!doctype linuxdoc system>\n\n";
2489 ofs << "<!doctype linuxdoc system [ "
2490 << params.preamble << sgml_includedfiles << " \n]>\n\n";
2493 if (params.options.empty())
2494 sgmlOpenTag(ofs, 0, top_element);
2496 string top = top_element;
2498 top += params.options;
2499 sgmlOpenTag(ofs, 0, top);
2503 ofs << "<!-- " << LYX_DOCVERSION
2504 << " created this file. For more info see http://www.lyx.org/"
2507 Paragraph::depth_type depth = 0; // paragraph depth
2508 Paragraph * par = paragraph;
2510 vector<string> environment_stack(5);
2513 LyXLayout const & style =
2514 textclasslist.Style(params.textclass,
2517 // treat <toc> as a special case for compatibility with old code
2518 if (par->getChar(0) == Paragraph::META_INSET) {
2519 Inset * inset = par->getInset(0);
2520 Inset::Code lyx_code = inset->lyxCode();
2521 if (lyx_code == Inset::TOC_CODE){
2522 string const temp = "toc";
2523 sgmlOpenTag(ofs, depth, temp);
2530 // environment tag closing
2531 for (; depth > par->params().depth(); --depth) {
2532 sgmlCloseTag(ofs, depth, environment_stack[depth]);
2533 environment_stack[depth].erase();
2536 // write opening SGML tags
2537 switch (style.latextype) {
2538 case LATEX_PARAGRAPH:
2539 if (depth == par->params().depth()
2540 && !environment_stack[depth].empty()) {
2541 sgmlCloseTag(ofs, depth, environment_stack[depth]);
2542 environment_stack[depth].erase();
2548 sgmlOpenTag(ofs, depth, style.latexname());
2553 linuxDocError(par, 0,
2554 _("Error : Wrong depth for"
2555 " LatexType Command.\n"));
2557 if (!environment_stack[depth].empty()){
2558 sgmlCloseTag(ofs, depth,
2559 environment_stack[depth]);
2563 environment_stack[depth].erase();
2564 sgmlOpenTag(ofs, depth, style.latexname());
2567 case LATEX_ENVIRONMENT:
2568 case LATEX_ITEM_ENVIRONMENT:
2569 if (depth == par->params().depth()
2570 && environment_stack[depth] != style.latexname()) {
2571 sgmlCloseTag(ofs, depth,
2572 environment_stack[depth]);
2573 environment_stack[depth].erase();
2575 if (depth < par->params().depth()) {
2576 depth = par->params().depth();
2577 environment_stack[depth].erase();
2579 if (environment_stack[depth] != style.latexname()) {
2581 sgmlOpenTag(ofs, depth, "p");
2583 sgmlOpenTag(ofs, depth, style.latexname());
2585 if (environment_stack.size() == depth + 1)
2586 environment_stack.push_back("!-- --");
2587 environment_stack[depth] = style.latexname();
2590 if (style.latexparam() == "CDATA")
2593 if (style.latextype == LATEX_ENVIRONMENT) break;
2595 if (style.labeltype == LABEL_MANUAL)
2600 sgmlOpenTag(ofs, depth + 1, item_name);
2603 sgmlOpenTag(ofs, depth, style.latexname());
2607 simpleLinuxDocOnePar(ofs, par, depth);
2612 // write closing SGML tags
2613 switch (style.latextype) {
2616 case LATEX_ENVIRONMENT:
2617 case LATEX_ITEM_ENVIRONMENT:
2618 if (style.latexparam() == "CDATA")
2622 sgmlCloseTag(ofs, depth, style.latexname());
2628 for (int i=depth; i >= 0; --i)
2629 sgmlCloseTag(ofs, depth, environment_stack[i]);
2633 sgmlCloseTag(ofs, 0, top_element);
2637 // How to check for successful close
2641 void Buffer::docBookHandleCaption(ostream & os, string & inner_tag,
2642 Paragraph::depth_type depth, int desc_on,
2645 Paragraph * tpar = par;
2647 && (tpar->layout != textclasslist.NumberOfLayout(params.textclass,
2649 tpar = tpar->next();
2652 tpar->layout == textclasslist.NumberOfLayout(params.textclass,
2653 "Caption").second) {
2654 sgmlOpenTag(os, depth + 1, inner_tag);
2656 simpleDocBookOnePar(os, extra_par, tpar,
2657 desc_on, depth + 2);
2658 sgmlCloseTag(os, depth+1, inner_tag);
2659 if (!extra_par.empty())
2665 // checks, if newcol chars should be put into this line
2666 // writes newline, if necessary.
2669 void linux_doc_line_break(ostream & os, string::size_type & colcount,
2670 string::size_type newcol)
2673 if (colcount > lyxrc.ascii_linelen) {
2675 colcount = newcol; // assume write after this call
2690 string tag_name(PAR_TAG const & pt) {
2692 case NONE: return "!-- --";
2693 case TT: return "tt";
2694 case SF: return "sf";
2695 case BF: return "bf";
2696 case IT: return "it";
2697 case SL: return "sl";
2698 case EM: return "em";
2705 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2707 p1 = static_cast<PAR_TAG>(p1 | p2);
2712 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2714 p1 = static_cast<PAR_TAG>( p1 & ~p2);
2722 // Handle internal paragraph parsing -- layout already processed.
2723 void Buffer::simpleLinuxDocOnePar(ostream & os,
2725 Paragraph::depth_type /*depth*/)
2727 LyXLayout const & style = textclasslist.Style(params.textclass,
2729 string::size_type char_line_count = 5; // Heuristic choice ;-)
2731 // gets paragraph main font
2734 if (style.labeltype == LABEL_MANUAL) {
2735 font_old = style.labelfont;
2738 font_old = style.font;
2742 LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
2743 LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
2744 LyXFont::FONT_SHAPE shape_type = LyXFont::UP_SHAPE;
2747 stack < PAR_TAG > tag_state;
2748 // parsing main loop
2749 for (Paragraph::size_type i = 0; i < par->size(); ++i) {
2751 PAR_TAG tag_close = NONE;
2752 list < PAR_TAG > tag_open;
2754 LyXFont const font = par->getFont(params, i);
2756 if (font_old.family() != font.family()) {
2757 switch (family_type) {
2758 case LyXFont::SANS_FAMILY:
2761 case LyXFont::TYPEWRITER_FAMILY:
2768 family_type = font.family();
2770 switch (family_type) {
2771 case LyXFont::SANS_FAMILY:
2772 tag_open.push_back(SF);
2774 case LyXFont::TYPEWRITER_FAMILY:
2775 tag_open.push_back(TT);
2782 if (font_old.series() != font.series()) {
2783 switch (series_type) {
2784 case LyXFont::BOLD_SERIES:
2791 series_type = font.series();
2793 switch (series_type) {
2794 case LyXFont::BOLD_SERIES:
2795 tag_open.push_back(BF);
2803 if (font_old.shape() != font.shape()) {
2804 switch (shape_type) {
2805 case LyXFont::ITALIC_SHAPE:
2808 case LyXFont::SLANTED_SHAPE:
2815 shape_type = font.shape();
2817 switch (shape_type) {
2818 case LyXFont::ITALIC_SHAPE:
2819 tag_open.push_back(IT);
2821 case LyXFont::SLANTED_SHAPE:
2822 tag_open.push_back(SL);
2829 if (font_old.emph() != font.emph()) {
2830 if (font.emph() == LyXFont::ON) {
2831 tag_open.push_back(EM);
2840 list < PAR_TAG > temp;
2841 while(!tag_state.empty() && tag_close ) {
2842 PAR_TAG k = tag_state.top();
2844 os << "</" << tag_name(k) << ">";
2851 for(list< PAR_TAG >::const_iterator j = temp.begin();
2852 j != temp.end(); ++j) {
2854 os << "<" << tag_name(*j) << ">";
2857 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
2858 j != tag_open.end(); ++j) {
2860 os << "<" << tag_name(*j) << ">";
2863 char c = par->getChar(i);
2865 if (c == Paragraph::META_INSET) {
2866 Inset * inset = par->getInset(i);
2867 inset->linuxdoc(this, os);
2874 font.latex() == LyXFont::ON ||
2876 style.latexparam() == "CDATA") {
2877 // "TeX"-Mode on == > SGML-Mode on.
2883 if (par->linuxDocConvertChar(c, sgml_string)
2884 && !style.free_spacing) {
2885 // in freespacing mode, spaces are
2886 // non-breaking characters
2887 if (desc_on) {// if char is ' ' then...
2890 linux_doc_line_break(os, char_line_count, 6);
2894 linux_doc_line_break(os, char_line_count, 1);
2899 char_line_count += sgml_string.length();
2905 while (!tag_state.empty()) {
2906 os << "</" << tag_name(tag_state.top()) << ">";
2910 // resets description flag correctly
2912 // <tag> not closed...
2913 linux_doc_line_break(os, char_line_count, 6);
2919 // Print an error message.
2920 void Buffer::linuxDocError(Paragraph * par, int pos,
2921 string const & message)
2923 // insert an error marker in text
2924 InsetError * new_inset = new InsetError(message);
2925 par->insertInset(pos, new_inset);
2929 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
2931 ofstream ofs(fname.c_str());
2933 WriteAlert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2937 Paragraph * par = paragraph;
2939 niceFile = nice; // this will be used by Insetincludes.
2941 LyXTextClass const & tclass =
2942 textclasslist.TextClass(params.textclass);
2944 LaTeXFeatures features(params, tclass.numLayouts());
2949 string top_element = textclasslist.LatexnameOfClass(params.textclass);
2952 string sgml_includedfiles = features.getIncludedFiles(fname);
2954 ofs << "<!doctype " << top_element
2955 << " public \"-//OASIS//DTD DocBook V3.1//EN\"";
2957 if (params.preamble.empty() && sgml_includedfiles.empty())
2960 ofs << "\n [ " << params.preamble
2961 << sgml_includedfiles << " \n]>\n\n";
2964 string top = top_element;
2966 top += params.language->code();
2969 if (!params.options.empty()) {
2971 top += params.options;
2973 sgmlOpenTag(ofs, 0, top);
2975 ofs << "<!-- DocBook file was created by " << LYX_DOCVERSION
2976 << "\n See http://www.lyx.org/ for more information -->\n";
2978 vector<string> environment_stack(10);
2979 vector<string> environment_inner(10);
2980 vector<string> command_stack(10);
2982 bool command_flag = false;
2983 Paragraph::depth_type command_depth = 0;
2984 Paragraph::depth_type command_base = 0;
2985 Paragraph::depth_type cmd_depth = 0;
2986 Paragraph::depth_type depth = 0; // paragraph depth
2989 string command_name;
2995 int desc_on = 0; // description mode
2997 LyXLayout const & style =
2998 textclasslist.Style(params.textclass,
3001 // environment tag closing
3002 for (; depth > par->params().depth(); --depth) {
3003 if (environment_inner[depth] != "!-- --") {
3004 item_name = "listitem";
3005 sgmlCloseTag(ofs, command_depth + depth,
3007 if (environment_inner[depth] == "varlistentry")
3008 sgmlCloseTag(ofs, depth+command_depth,
3009 environment_inner[depth]);
3011 sgmlCloseTag(ofs, depth + command_depth,
3012 environment_stack[depth]);
3013 environment_stack[depth].erase();
3014 environment_inner[depth].erase();
3017 if (depth == par->params().depth()
3018 && environment_stack[depth] != style.latexname()
3019 && !environment_stack[depth].empty()) {
3020 if (environment_inner[depth] != "!-- --") {
3021 item_name= "listitem";
3022 sgmlCloseTag(ofs, command_depth+depth,
3024 if (environment_inner[depth] == "varlistentry")
3026 depth + command_depth,
3027 environment_inner[depth]);
3030 sgmlCloseTag(ofs, depth + command_depth,
3031 environment_stack[depth]);
3033 environment_stack[depth].erase();
3034 environment_inner[depth].erase();
3037 // Write opening SGML tags.
3038 switch (style.latextype) {
3039 case LATEX_PARAGRAPH:
3040 sgmlOpenTag(ofs, depth + command_depth,
3046 linuxDocError(par, 0,
3047 _("Error : Wrong depth for "
3048 "LatexType Command.\n"));
3050 command_name = style.latexname();
3052 sgmlparam = style.latexparam();
3053 c_params = split(sgmlparam, c_depth,'|');
3055 cmd_depth = lyx::atoi(c_depth);
3058 if (cmd_depth < command_base) {
3059 for (Paragraph::depth_type j = command_depth; j >= command_base; --j)
3060 sgmlCloseTag(ofs, j, command_stack[j]);
3061 command_depth = command_base = cmd_depth;
3062 } else if (cmd_depth <= command_depth) {
3063 for (int j = command_depth; j >= int(cmd_depth); --j)
3064 sgmlCloseTag(ofs, j, command_stack[j]);
3065 command_depth = cmd_depth;
3067 command_depth = cmd_depth;
3069 command_depth = command_base = cmd_depth;
3070 command_flag = true;
3072 if (command_stack.size() == command_depth + 1)
3073 command_stack.push_back(string());
3074 command_stack[command_depth] = command_name;
3076 // treat label as a special case for
3077 // more WYSIWYM handling.
3078 if (par->getChar(0) == Paragraph::META_INSET) {
3079 Inset * inset = par->getInset(0);
3080 Inset::Code lyx_code = inset->lyxCode();
3081 if (lyx_code == Inset::LABEL_CODE){
3082 command_name += " id=\"";
3083 command_name += (static_cast<InsetCommand *>(inset))->getContents();
3084 command_name += "\"";
3089 sgmlOpenTag(ofs, depth + command_depth, command_name);
3090 if (c_params.empty())
3091 item_name = "title";
3093 item_name = c_params;
3094 sgmlOpenTag(ofs, depth + 1 + command_depth, item_name);
3097 case LATEX_ENVIRONMENT:
3098 case LATEX_ITEM_ENVIRONMENT:
3099 if (depth < par->params().depth()) {
3100 depth = par->params().depth();
3101 environment_stack[depth].erase();
3104 if (environment_stack[depth] != style.latexname()) {
3105 if(environment_stack.size() == depth + 1) {
3106 environment_stack.push_back("!-- --");
3107 environment_inner.push_back("!-- --");
3109 environment_stack[depth] = style.latexname();
3110 environment_inner[depth] = "!-- --";
3111 sgmlOpenTag(ofs, depth + command_depth,
3112 environment_stack[depth]);
3114 if (environment_inner[depth] != "!-- --") {
3115 item_name= "listitem";
3117 command_depth + depth,
3119 if (environment_inner[depth] == "varlistentry")
3121 depth + command_depth,
3122 environment_inner[depth]);
3126 if (style.latextype == LATEX_ENVIRONMENT) {
3127 if (!style.latexparam().empty()) {
3128 if(style.latexparam() == "CDATA")
3131 sgmlOpenTag(ofs, depth + command_depth,
3132 style.latexparam());
3137 desc_on = (style.labeltype == LABEL_MANUAL);
3140 environment_inner[depth]= "varlistentry";
3142 environment_inner[depth]= "listitem";
3144 sgmlOpenTag(ofs, depth + 1 + command_depth,
3145 environment_inner[depth]);
3149 sgmlOpenTag(ofs, depth + 1 + command_depth,
3153 sgmlOpenTag(ofs, depth + 1 + command_depth,
3158 sgmlOpenTag(ofs, depth + command_depth,
3164 simpleDocBookOnePar(ofs, extra_par, par, desc_on,
3165 depth + 1 + command_depth);
3169 // write closing SGML tags
3170 switch (style.latextype) {
3172 if (c_params.empty())
3176 sgmlCloseTag(ofs, depth + command_depth, end_tag);
3178 case LATEX_ENVIRONMENT:
3179 if (!style.latexparam().empty()) {
3180 if(style.latexparam() == "CDATA")
3183 sgmlCloseTag(ofs, depth + command_depth,
3184 style.latexparam());
3187 case LATEX_ITEM_ENVIRONMENT:
3188 if (desc_on == 1) break;
3190 sgmlCloseTag(ofs, depth + 1 + command_depth, end_tag);
3192 case LATEX_PARAGRAPH:
3193 sgmlCloseTag(ofs, depth + command_depth, style.latexname());
3196 sgmlCloseTag(ofs, depth + command_depth, style.latexname());
3202 for (int d = depth; d >= 0; --d) {
3203 if (!environment_stack[depth].empty()) {
3204 if (environment_inner[depth] != "!-- --") {
3205 item_name = "listitem";
3206 sgmlCloseTag(ofs, command_depth + depth,
3208 if (environment_inner[depth] == "varlistentry")
3209 sgmlCloseTag(ofs, depth + command_depth,
3210 environment_inner[depth]);
3213 sgmlCloseTag(ofs, depth + command_depth,
3214 environment_stack[depth]);
3218 for (int j = command_depth; j >= 0 ; --j)
3219 if (!command_stack[j].empty())
3220 sgmlCloseTag(ofs, j, command_stack[j]);
3223 sgmlCloseTag(ofs, 0, top_element);
3226 // How to check for successful close
3230 void Buffer::simpleDocBookOnePar(ostream & os, string & extra,
3231 Paragraph * par, int & desc_on,
3232 Paragraph::depth_type depth) const
3234 bool emph_flag = false;
3236 LyXLayout const & style = textclasslist.Style(params.textclass,
3239 LyXFont font_old = style.labeltype == LABEL_MANUAL ? style.labelfont : style.font;
3241 int char_line_count = depth;
3242 //if (!style.free_spacing)
3243 // os << string(depth,' ');
3245 // parsing main loop
3246 for (Paragraph::size_type i = 0;
3247 i < par->size(); ++i) {
3248 LyXFont font = par->getFont(params, i);
3250 // handle <emphasis> tag
3251 if (font_old.emph() != font.emph()) {
3252 if (font.emph() == LyXFont::ON) {
3256 os << "</emphasis>";
3261 char c = par->getChar(i);
3263 if (c == Paragraph::META_INSET) {
3264 Inset * inset = par->getInset(i);
3266 inset->docBook(this, ost);
3267 string tmp_out = ost.str().c_str();
3270 // This code needs some explanation:
3271 // Two insets are treated specially
3272 // label if it is the first element in a command paragraph
3274 // graphics inside tables or figure floats can't go on
3275 // title (the equivalente in latex for this case is caption
3276 // and title should come first
3279 if (desc_on!= 3 || i!= 0) {
3280 if (!tmp_out.empty() && tmp_out[0] == '@') {
3282 extra += frontStrip(tmp_out, '@');
3284 os << frontStrip(tmp_out, '@');
3290 } else if (font.latex() == LyXFont::ON) {
3291 // "TeX"-Mode on ==> SGML-Mode on.
3298 if (par->linuxDocConvertChar(c, sgml_string)
3299 && !style.free_spacing) { // in freespacing
3301 // non-breaking characters
3305 os << "\n</term><listitem><para>";
3318 os << "</emphasis>";
3321 // resets description flag correctly
3323 // <term> not closed...
3326 if(style.free_spacing) os << '\n';
3330 // This should be enabled when the Chktex class is implemented. (Asger)
3331 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
3332 // Other flags: -wall -v0 -x
3333 int Buffer::runChktex()
3335 if (!users->text) return 0;
3337 users->owner()->prohibitInput();
3339 // get LaTeX-Filename
3340 string const name = getLatexName();
3341 string path = OnlyPath(filename);
3343 string const org_path = path;
3344 if (lyxrc.use_tempdir || (IsDirWriteable(path) < 1)) {
3348 Path p(path); // path to LaTeX file
3349 users->owner()->message(_("Running chktex..."));
3351 // Remove all error insets
3352 bool const removedErrorInsets = users->removeAutoInsets();
3354 // Generate the LaTeX file if neccessary
3355 makeLaTeXFile(name, org_path, false);
3358 Chktex chktex(lyxrc.chktex_command, name, filepath);
3359 int res = chktex.run(terr); // run chktex
3362 WriteAlert(_("chktex did not work!"),
3363 _("Could not run with file:"), name);
3364 } else if (res > 0) {
3365 // Insert all errors as errors boxes
3366 users->insertErrors(terr);
3369 // if we removed error insets before we ran chktex or if we inserted
3370 // error insets after we ran chktex, this must be run:
3371 if (removedErrorInsets || res){
3373 users->fitCursor(users->text);
3375 users->owner()->allowInput();
3381 void Buffer::validate(LaTeXFeatures & features) const
3383 Paragraph * par = paragraph;
3384 LyXTextClass const & tclass =
3385 textclasslist.TextClass(params.textclass);
3387 // AMS Style is at document level
3389 features.amsstyle = (params.use_amsmath ||
3390 tclass.provides(LyXTextClass::amsmath));
3393 // We don't use "lyxerr.debug" because of speed. (Asger)
3394 if (lyxerr.debugging(Debug::LATEX))
3395 lyxerr << "Paragraph: " << par << endl;
3397 // Now just follow the list of paragraphs and run
3398 // validate on each of them.
3399 par->validate(features);
3401 // and then the next paragraph
3405 // the bullet shapes are buffer level not paragraph level
3406 // so they are tested here
3407 for (int i = 0; i < 4; ++i) {
3408 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
3409 int const font = params.user_defined_bullets[i].getFont();
3411 int const c = params
3412 .user_defined_bullets[i]
3419 features.latexsym = true;
3421 } else if (font == 1) {
3422 features.amssymb = true;
3423 } else if ((font >= 2 && font <= 5)) {
3424 features.pifont = true;
3429 if (lyxerr.debugging(Debug::LATEX)) {
3430 features.showStruct();
3435 void Buffer::setPaperStuff()
3437 params.papersize = BufferParams::PAPER_DEFAULT;
3438 char const c1 = params.paperpackage;
3439 if (c1 == BufferParams::PACKAGE_NONE) {
3440 char const c2 = params.papersize2;
3441 if (c2 == BufferParams::VM_PAPER_USLETTER)
3442 params.papersize = BufferParams::PAPER_USLETTER;
3443 else if (c2 == BufferParams::VM_PAPER_USLEGAL)
3444 params.papersize = BufferParams::PAPER_LEGALPAPER;
3445 else if (c2 == BufferParams::VM_PAPER_USEXECUTIVE)
3446 params.papersize = BufferParams::PAPER_EXECUTIVEPAPER;
3447 else if (c2 == BufferParams::VM_PAPER_A3)
3448 params.papersize = BufferParams::PAPER_A3PAPER;
3449 else if (c2 == BufferParams::VM_PAPER_A4)
3450 params.papersize = BufferParams::PAPER_A4PAPER;
3451 else if (c2 == BufferParams::VM_PAPER_A5)
3452 params.papersize = BufferParams::PAPER_A5PAPER;
3453 else if ((c2 == BufferParams::VM_PAPER_B3) || (c2 == BufferParams::VM_PAPER_B4) ||
3454 (c2 == BufferParams::VM_PAPER_B5))
3455 params.papersize = BufferParams::PAPER_B5PAPER;
3456 } else if ((c1 == BufferParams::PACKAGE_A4) || (c1 == BufferParams::PACKAGE_A4WIDE) ||
3457 (c1 == BufferParams::PACKAGE_WIDEMARGINSA4))
3458 params.papersize = BufferParams::PAPER_A4PAPER;
3462 // This function should be in Buffer because it's a buffer's property (ale)
3463 string const Buffer::getIncludeonlyList(char delim)
3466 for (inset_iterator it = inset_iterator_begin();
3467 it != inset_iterator_end(); ++it) {
3468 if ((*it)->lyxCode() == Inset::INCLUDE_CODE) {
3469 InsetInclude * insetinc =
3470 static_cast<InsetInclude *>(*it);
3471 if (insetinc->isIncludeOnly()) {
3474 lst += insetinc->getRelFileBaseName();
3478 lyxerr[Debug::INFO] << "Includeonly(" << lst << ')' << endl;
3483 vector<string> const Buffer::getLabelList()
3485 /// if this is a child document and the parent is already loaded
3486 /// Use the parent's list instead [ale990407]
3487 if (!params.parentname.empty()
3488 && bufferlist.exists(params.parentname)) {
3489 Buffer * tmp = bufferlist.getBuffer(params.parentname);
3491 return tmp->getLabelList();
3494 vector<string> label_list;
3495 for (inset_iterator it = inset_iterator_begin();
3496 it != inset_iterator_end(); ++it) {
3497 vector<string> const l = (*it)->getLabelList();
3498 label_list.insert(label_list.end(), l.begin(), l.end());
3504 Buffer::Lists const Buffer::getLists() const
3507 Paragraph * par = paragraph;
3509 LyXTextClassList::size_type cap;
3510 boost::tie(found, cap) = textclasslist
3511 .NumberOfLayout(params.textclass, "Caption");
3514 char const labeltype =
3515 textclasslist.Style(params.textclass,
3516 par->getLayout()).labeltype;
3518 if (labeltype >= LABEL_COUNTER_CHAPTER
3519 && labeltype <= LABEL_COUNTER_CHAPTER + params.tocdepth) {
3520 // insert this into the table of contents
3521 SingleList & item = l["TOC"];
3524 textclasslist.TextClass(params.textclass).maxcounter());
3525 item.push_back(TocItem(par, depth, par->asString(this, true)));
3527 // For each paragrph, traverse its insets and look for
3531 Paragraph::inset_iterator it =
3532 par->inset_iterator_begin();
3533 Paragraph::inset_iterator end =
3534 par->inset_iterator_end();
3536 for (; it != end; ++it) {
3537 if ((*it)->lyxCode() == Inset::FLOAT_CODE) {
3539 static_cast<InsetFloat*>(*it);
3541 string const type = il->type();
3543 // Now find the caption in the float...
3544 // We now tranverse the paragraphs of
3546 Paragraph * tmp = il->inset.paragraph();
3548 if (tmp->layout == cap) {
3549 SingleList & item = l[type];
3551 tostr(item.size()+1) + ". " + tmp->asString(this, false);
3552 item.push_back(TocItem(tmp, 0 , str));
3559 lyxerr << "caption not found" << endl;
3568 // This is also a buffer property (ale)
3569 vector<pair<string, string> > const Buffer::getBibkeyList()
3571 /// if this is a child document and the parent is already loaded
3572 /// Use the parent's list instead [ale990412]
3573 if (!params.parentname.empty() && bufferlist.exists(params.parentname)) {
3574 Buffer * tmp = bufferlist.getBuffer(params.parentname);
3576 return tmp->getBibkeyList();
3579 vector<pair<string, string> > keys;
3580 Paragraph * par = paragraph;
3583 keys.push_back(pair<string, string>(par->bibkey->getContents(),
3584 par->asString(this, false)));
3588 // Might be either using bibtex or a child has bibliography
3590 for (inset_iterator it = inset_iterator_begin();
3591 it != inset_iterator_end(); ++it) {
3592 // Search for Bibtex or Include inset
3593 if ((*it)->lyxCode() == Inset::BIBTEX_CODE) {
3594 vector<pair<string,string> > tmp =
3595 static_cast<InsetBibtex*>(*it)->getKeys(this);
3596 keys.insert(keys.end(), tmp.begin(), tmp.end());
3597 } else if ((*it)->lyxCode() == Inset::INCLUDE_CODE) {
3598 vector<pair<string,string> > const tmp =
3599 static_cast<InsetInclude*>(*it)->getKeys();
3600 keys.insert(keys.end(), tmp.begin(), tmp.end());
3609 bool Buffer::isDepClean(string const & name) const
3611 DEPCLEAN * item = dep_clean;
3612 while (item && item->master != name)
3614 if (!item) return true;
3619 void Buffer::markDepClean(string const & name)
3622 dep_clean = new DEPCLEAN;
3623 dep_clean->clean = true;
3624 dep_clean->master = name;
3625 dep_clean->next = 0;
3627 DEPCLEAN * item = dep_clean;
3628 while (item && item->master != name)
3633 item = new DEPCLEAN;
3635 item->master = name;
3642 bool Buffer::dispatch(string const & command)
3644 // Split command string into command and argument
3646 string line = frontStrip(command);
3647 string const arg = strip(frontStrip(split(line, cmd, ' ')));
3649 return dispatch(lyxaction.LookupFunc(cmd), arg);
3653 bool Buffer::dispatch(int action, string const & argument)
3655 bool dispatched = true;
3658 Exporter::Export(this, argument, false);
3668 void Buffer::resizeInsets(BufferView * bv)
3670 /// then remove all LyXText in text-insets
3671 Paragraph * par = paragraph;
3672 for (; par; par = par->next()) {
3673 par->resizeInsetsLyXText(bv);
3678 void Buffer::redraw()
3681 users->fitCursor(users->text);
3685 void Buffer::changeLanguage(Language const * from, Language const * to)
3688 Paragraph * par = paragraph;
3690 par->changeLanguage(params, from, to);
3696 bool Buffer::isMultiLingual()
3698 Paragraph * par = paragraph;
3700 if (par->isMultiLingual(params))
3708 Buffer::inset_iterator::inset_iterator(Paragraph * paragraph,
3709 Paragraph::size_type pos)
3712 it = par->InsetIterator(pos);
3713 if (it == par->inset_iterator_end()) {
3720 void Buffer::inset_iterator::setParagraph()
3723 it = par->inset_iterator_begin();
3724 if (it != par->inset_iterator_end())
3729 // We maintain an invariant that whenever par = 0 then it = 0
3733 Inset * Buffer::getInsetFromID(int id_arg) const
3735 for (inset_iterator it = inset_const_iterator_begin();
3736 it != inset_const_iterator_end(); ++it)
3738 if ((*it)->id() == id_arg)
3740 Inset * in = (*it)->getInsetFromID(id_arg);
3748 Paragraph * Buffer::getParFromID(int id) const
3750 if (id < 0) return 0;
3751 Paragraph * par = paragraph;
3753 if (par->id() == id) {
3756 Paragraph * tmp = par->getParFromID(id);