]> git.lyx.org Git - lyx.git/blobdiff - src/buffer.C
Fix natbib bug spotted by JMarc.
[lyx.git] / src / buffer.C
index 4f5e71c5b9200679ae6848a881f4315d746666a7..6a3c50a5d69ad9b8e3c3961b04405fe98a8279ba 100644 (file)
@@ -45,6 +45,7 @@
 #include "lyxtextclasslist.h"
 #include "sgml.h"
 #include "paragraph_funcs.h"
+#include "author.h"
 
 #include "frontends/LyXView.h"
 
@@ -97,6 +98,7 @@
 #include "support/FileInfo.h"
 #include "support/lyxmanip.h"
 #include "support/lyxalgo.h" // for lyx::count
+#include "support/lyxtime.h"
 
 #include <boost/bind.hpp>
 #include <boost/tuple/tuple.hpp>
@@ -148,13 +150,13 @@ extern BufferList bufferlist;
 
 namespace {
 
-const int LYX_FORMAT = 221;
+const int LYX_FORMAT = 222;
 
 } // namespace anon
 
 Buffer::Buffer(string const & file, bool ronly)
        : niceFile(true), lyx_clean(true), bak_clean(true),
-         unnamed(false), dep_clean(0), read_only(ronly),
+         unnamed(false), read_only(ronly),
          filename_(file), users(0)
 {
        lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
@@ -165,6 +167,9 @@ Buffer::Buffer(string const & file, bool ronly)
        } else {
                tmppath.erase();
        }
+
+       // set initial author
+       authorlist.record(Author(lyxrc.user_name, lyxrc.user_email)); 
 }
 
 
@@ -227,10 +232,10 @@ pair<Buffer::LogType, string> const Buffer::getLogName() const
 
        if (b_fi.exist() &&
            (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
-               lyxerr[Debug::FILES] << "Log name calculated as : " << bname << endl;
+               lyxerr[Debug::FILES] << "Log name calculated as: " << bname << endl;
                return make_pair(Buffer::buildlog, bname);
        }
-       lyxerr[Debug::FILES] << "Log name calculated as : " << fname << endl;
+       lyxerr[Debug::FILES] << "Log name calculated as: " << fname << endl;
        return make_pair(Buffer::latexlog, fname);
 }
 
@@ -245,6 +250,12 @@ void Buffer::setReadonly(bool flag)
 }
 
 
+AuthorList & Buffer::authors()
+{
+       return authorlist;
+}
+
+
 /// Update window titles of all users
 // Should work on a list
 void Buffer::updateTitles() const
@@ -282,6 +293,7 @@ string last_inset_read;
 #endif
 int unknown_layouts;
 int unknown_tokens;
+vector<int> author_ids;
 
 } // anon
 
@@ -293,11 +305,12 @@ int unknown_tokens;
 // changed to be public and have one parameter
 // if par = 0 normal behavior
 // else insert behavior
-// Returns false if "\the_end" is not read for formats >= 2.13. (Asger)
+// Returns false if "\the_end" is not read (Asger)
 bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
 {
        unknown_layouts = 0;
        unknown_tokens = 0;
+       author_ids.clear();
 
        int pos = 0;
        Paragraph::depth_type depth = 0;
@@ -339,7 +352,7 @@ bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
                if (token.empty()) continue;
 
                lyxerr[Debug::PARSER] << "Handling token: `"
-                                     << token << "'" << endl;
+                                     << token << '\'' << endl;
 
                the_end_read =
                        parseSingleLyXformat2Token(lex, par, first_par,
@@ -360,8 +373,13 @@ bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
                        s += tostr(unknown_layouts);
                        s += _(" paragraphs");
                }
+#if USE_BOOST_FORMAT
                Alert::alert(_("Textclass Loading Error!"), s,
                           boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
+#else
+               Alert::alert(_("Textclass Loading Error!"), s,
+                            _("When reading ") + fileName());
+#endif
        }
 
        if (unknown_tokens > 0) {
@@ -372,14 +390,26 @@ bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
                        s += tostr(unknown_tokens);
                        s += _(" unknown tokens");
                }
+#if USE_BOOST_FORMAT
                Alert::alert(_("Textclass Loading Error!"), s,
                           boost::io::str(boost::format(_("When reading %1$s")) % fileName()));
+#else
+               Alert::alert(_("Textclass Loading Error!"), s,
+                            _("When reading ") +  fileName());
+#endif
        }
 
        return the_end_read;
 }
 
 
+namespace {
+       // This stuff is, in the traditional LyX terminology, Super UGLY
+       // but this code is too b0rken to admit of a better solution yet
+       Change current_change;
+};
+
 bool
 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                                   Paragraph *& first_par,
@@ -398,7 +428,7 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
        if (token[0] != '\\') {
                for (string::const_iterator cit = token.begin();
                     cit != token.end(); ++cit) {
-                       par->insertChar(pos, (*cit), font);
+                       par->insertChar(pos, (*cit), font, current_change);
                        ++pos;
                }
        } else if (token == "\\layout") {
@@ -488,6 +518,8 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                        else {
                                par = new Paragraph(par);
                                par->layout(params.getLyXTextClass().defaultLayout());
+                               if (params.tracking_changes)
+                                       par->trackChanges();
                        }
                        pos = 0;
                        par->layout(params.getLyXTextClass()[layoutname]);
@@ -564,11 +596,11 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                if (layout->free_spacing || par->isFreeSpacing()) {
                        if (lex.isOK()) {
                                lex.next();
-                               string next_token = lex.getString();
+                               string const next_token = lex.getString();
                                if (next_token == "\\-") {
-                                       par->insertChar(pos, '-', font);
+                                       par->insertChar(pos, '-', font, current_change);
                                } else if (next_token == "~") {
-                                       par->insertChar(pos, ' ', font);
+                                       par->insertChar(pos, ' ', font, current_change);
                                } else {
                                        lex.printError("Token `$$Token' "
                                                       "is in free space "
@@ -579,16 +611,16 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                } else {
                        Inset * inset = new InsetSpecialChar;
                        inset->read(this, lex);
-                       par->insertInset(pos, inset, font);
+                       par->insertInset(pos, inset, font, current_change);
                }
                ++pos;
        } else if (token == "\\i") {
                Inset * inset = new InsetLatexAccent;
                inset->read(this, lex);
-               par->insertInset(pos, inset, font);
+               par->insertInset(pos, inset, font, current_change);
                ++pos;
        } else if (token == "\\backslash") {
-               par->insertChar(pos, '\\', font);
+               par->insertChar(pos, '\\', font, current_change);
                ++pos;
        } else if (token == "\\begin_deeper") {
                ++depth;
@@ -608,9 +640,17 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                if (pp.first) {
                        params.textclass = pp.second;
                } else {
+#if USE_BOOST_FORMAT
                        Alert::alert(_("Textclass error"),
                                boost::io::str(boost::format(_("The document uses an unknown textclass \"%1$s\".")) % lex.getString()),
-                               _("LyX will not be able to produce output correctly."));
+                               _("-- substituting default."));
+#else
+                       Alert::alert(
+                               _("Textclass error"),
+                               _("The document uses an unknown textclass ")
+                               + lex.getString(),
+                               _("-- substituting default."));
+#endif
                        params.textclass = 0;
                }
                if (!params.getLyXTextClass().load()) {
@@ -619,10 +659,17 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                        // or stop loading the file.
                        // I can substitute but I don't see how I can
                        // stop loading... ideas??  ARRae980418
+#if USE_BOOST_FORMAT
                        Alert::alert(_("Textclass Loading Error!"),
                                   boost::io::str(boost::format(_("Can't load textclass %1$s")) %
                                   params.getLyXTextClass().name()),
-                                  _("-- substituting default"));
+                                  _("-- substituting default."));
+#else
+                       Alert::alert(_("Textclass Loading Error!"),
+                                    _("Can't load textclass ")
+                                    + params.getLyXTextClass().name(),
+                                    _("-- substituting default."));
+#endif
                        params.textclass = 0;
                }
        } else if (token == "\\options") {
@@ -731,6 +778,21 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
        } else if (token == "\\use_numerical_citations") {
                lex.nextToken();
                params.use_numerical_citations = lex.getInteger();
+       } else if (token == "\\tracking_changes") {
+               lex.nextToken();
+               params.tracking_changes = lex.getInteger();
+               // mark the first paragraph
+               if (params.tracking_changes)
+                       par->trackChanges();
+       } else if (token == "\\author") {
+               lex.nextToken();
+               istringstream ss(lex.getString());
+               Author a;
+               ss >> a;
+               int aid(authorlist.record(a)); 
+               lyxerr << "aid is " << aid << endl;
+               lyxerr << "listed aid is " << author_ids.size() << endl;
+               author_ids.push_back(authorlist.record(a));
        } else if (token == "\\paperorientation") {
                int tmpret = lex.findToken(string_orientation);
                if (tmpret == -1)
@@ -904,16 +966,37 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                par->params().labelWidthString(lex.getString());
                // do not delete this token, it is still needed!
        } else if (token == "\\newline") {
-               par->insertChar(pos, Paragraph::META_NEWLINE, font);
+               par->insertChar(pos, Paragraph::META_NEWLINE, font, current_change);
                ++pos;
        } else if (token == "\\LyXTable") {
                Inset * inset = new InsetTabular(*this);
                inset->read(this, lex);
-               par->insertInset(pos, inset, font);
+               par->insertInset(pos, inset, font, current_change);
                ++pos;
        } else if (token == "\\hfill") {
-               par->insertChar(pos, Paragraph::META_HFILL, font);
+               par->insertChar(pos, Paragraph::META_HFILL, font, current_change);
                ++pos;
+       } else if (token == "\\change_unchanged") {
+               // Hack ! Needed for empty paragraphs :/
+               if (!pos)
+                       par->cleanChanges();
+               current_change = Change(Change::UNCHANGED);
+       } else if (token == "\\change_inserted") {
+               lex.nextToken();
+               istringstream istr(lex.getString());
+               int aid;
+               lyx::time_type ct;
+               istr >> aid;
+               istr >> ct;
+               current_change = Change(Change::INSERTED, author_ids[aid], ct);
+       } else if (token == "\\change_deleted") {
+               lex.nextToken();
+               istringstream istr(lex.getString());
+               int aid;
+               lyx::time_type ct;
+               istr >> aid;
+               istr >> ct;
+               current_change = Change(Change::DELETED, author_ids[aid], ct);
        } else if (token == "\\bibitem") {  // ale970302
                if (!par->bibkey) {
                        InsetCommandParams p("bibitem", "dummy");
@@ -926,8 +1009,14 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                // This should be insurance for the future: (Asger)
                ++unknown_tokens;
                lex.eatLine();
-               string const s = boost::io::str(boost::format(_("Unknown token: %1$s %2$s\n")) % token
-                       % lex.text());
+#if USE_BOOST_FORMAT
+               boost::format fmt(_("Unknown token: %1$s %2$s\n"));
+               fmt % token % lex.text();
+               string const s = fmt.str();
+#else
+               string const s = _("Unknown token: ") + token
+                       + ' ' + lex.text() + '\n';
+#endif
                // we can do this here this way because we're actually reading
                // the buffer and don't care about LyXText right now.
                InsetError * new_inset = new InsetError(s);
@@ -939,6 +1028,7 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
        return the_end_read;
 }
 
+
 // needed to insert the selection
 void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
                                 LyXFont const & fn,string const & str) const
@@ -973,13 +1063,13 @@ void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
                } else if (*cit == '\t') {
                        if (!layout->free_spacing && !par->isFreeSpacing()) {
                                // tabs are like spaces here
-                               par->insertChar(pos, ' ', font);
+                               par->insertChar(pos, ' ', font, current_change);
                                ++pos;
                                space_inserted = true;
                        } else {
                                const pos_type nb = 8 - pos % 8;
                                for (pos_type a = 0; a < nb ; ++a) {
-                                       par->insertChar(pos, ' ', font);
+                                       par->insertChar(pos, ' ', font, current_change);
                                        ++pos;
                                }
                                space_inserted = true;
@@ -1123,7 +1213,7 @@ void Buffer::readInset(LyXLex & lex, Paragraph *& par,
        }
 
        if (inset) {
-               par->insertInset(pos, inset, font);
+               par->insertInset(pos, inset, font, current_change);
                ++pos;
        }
 }
@@ -1137,7 +1227,7 @@ bool Buffer::readFile(LyXLex & lex, string const & filename, Paragraph * par)
                if (token == "\\lyxformat") { // the first token _must_ be...
                        lex.eatLine();
                        string tmp_format = lex.getString();
-                       //lyxerr << "LyX Format: `" << tmp_format << "'" << endl;
+                       //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
                        // if present remove ".," from string.
                        string::size_type dot = tmp_format.find_first_of(".,");
                        //lyxerr << "           dot found at " << dot << endl;
@@ -1150,7 +1240,7 @@ bool Buffer::readFile(LyXLex & lex, string const & filename, Paragraph * par)
                        } else if (file_format > LYX_FORMAT) {
                                // future format
                                Alert::alert(_("Warning!"),
-                                       _("The file was created with a newer version of"
+                                       _("The file was created with a newer version of "
                                        "LyX. This is likely to cause problems."));
 
                        } else if (file_format < LYX_FORMAT) {
@@ -1169,10 +1259,11 @@ bool Buffer::readFile(LyXLex & lex, string const & filename, Paragraph * par)
                                                return false;
                                        }
                                        command += " -t"
-                                               +tostr(LYX_FORMAT)+" "
+                                               +tostr(LYX_FORMAT) + ' '
                                                + QuoteName(filename);
                                        lyxerr[Debug::INFO] << "Running '"
-                                                           << command << "'" << endl;
+                                                           << command << '\''
+                                                           << endl;
                                        cmd_ret const ret = RunCommand(command);
                                        if (ret.first) {
                                                Alert::alert(_("ERROR!"),
@@ -1183,7 +1274,7 @@ bool Buffer::readFile(LyXLex & lex, string const & filename, Paragraph * par)
                                        istringstream is(STRCONV(ret.second));
                                        LyXLex tmplex(0, 0);
                                        tmplex.setStream(is);
-                                       return readFile(tmplex, string());
+                                       return readFile(tmplex, string(), par);
                                } else {
                                        // This code is reached if lyx2lyx failed (for
                                        // some reason) to change the file format of
@@ -1323,6 +1414,15 @@ bool Buffer::writeFile(string const & fname) const
        // now write out the buffer paramters.
        params.writeFile(ofs);
 
+       // if we're tracking, list all possible authors
+       if (params.tracking_changes) {
+               AuthorList::Authors::const_iterator it = authorlist.begin();
+               AuthorList::Authors::const_iterator end = authorlist.end();
+               for (; it != end; ++it) {
+                       ofs << "\\author " << it->second << "\n";
+               }
+       }
        Paragraph::depth_type depth = 0;
 
        // this will write out all the paragraphs
@@ -1493,7 +1593,7 @@ string const Buffer::asciiParagraph(Paragraph const & par,
                default:
                {
                        string const parlab = par.params().labelString();
-                       buffer << parlab << " ";
+                       buffer << parlab << ' ';
                        currlinelen += parlab.length() + 1;
                }
                break;
@@ -2063,6 +2163,15 @@ void Buffer::makeLaTeXFile(ostream & os,
 
                preamble += "\\makeatother\n";
 
+               // dvipost settings come after everything else
+               if (params.tracking_changes) {
+                       preamble += "\\dvipostlayout\n";
+                       preamble += "\\dvipost{osstart color push Red}\n";
+                       preamble += "\\dvipost{osend color pop}\n";
+                       preamble += "\\dvipost{cbstart color push Blue}\n";
+                       preamble += "\\dvipost{cbend color pop} \n";
+               }
+
                os << preamble;
 
                if (only_preamble)
@@ -2107,8 +2216,9 @@ void Buffer::makeLaTeXFile(ostream & os,
        // Just to be sure. (Asger)
        texrow.newline();
 
-       lyxerr[Debug::INFO] << "Finished making latex file." << endl;
-       lyxerr[Debug::INFO] << "Row count was " << texrow.rows()-1 << "." << endl;
+       lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
+       lyxerr[Debug::INFO] << "Row count was " << texrow.rows() - 1
+                           << '.' << endl;
 
        // we want this to be true outside previews (for insetexternal)
        niceFile = true;
@@ -2240,7 +2350,7 @@ void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
                        sgml::openTag(ofs, 0, false, top_element);
                else {
                        string top = top_element;
-                       top += " ";
+                       top += ' ';
                        top += params.options;
                        sgml::openTag(ofs, 0, false, top);
                }
@@ -2294,8 +2404,7 @@ void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
                case LATEX_COMMAND:
                        if (depth!= 0)
                                sgmlError(par, 0,
-                                         _("Error : Wrong depth for"
-                                           " LatexType Command.\n"));
+                                         _("Error: Wrong depth for LatexType Command.\n"));
 
                        if (!environment_stack[depth].empty()) {
                                sgml::closeTag(ofs, depth, false, environment_stack[depth]);
@@ -2565,7 +2674,7 @@ void Buffer::simpleLinuxDocOnePar(ostream & os,
                while (!tag_state.empty() && tag_close) {
                        PAR_TAG k =  tag_state.top();
                        tag_state.pop();
-                       os << "</" << tag_name(k) << ">";
+                       os << "</" << tag_name(k) << '>';
                        if (tag_close & k)
                                reset(tag_close,k);
                        else
@@ -2575,13 +2684,13 @@ void Buffer::simpleLinuxDocOnePar(ostream & os,
                for(list< PAR_TAG >::const_iterator j = temp.begin();
                    j != temp.end(); ++j) {
                        tag_state.push(*j);
-                       os << "<" << tag_name(*j) << ">";
+                       os << '<' << tag_name(*j) << '>';
                }
 
                for(list< PAR_TAG >::const_iterator j = tag_open.begin();
                    j != tag_open.end(); ++j) {
                        tag_state.push(*j);
-                       os << "<" << tag_name(*j) << ">";
+                       os << '<' << tag_name(*j) << '>';
                }
 
                char c = par->getChar(i);
@@ -2624,7 +2733,7 @@ void Buffer::simpleLinuxDocOnePar(ostream & os,
        }
 
        while (!tag_state.empty()) {
-               os << "</" << tag_name(tag_state.top()) << ">";
+               os << "</" << tag_name(tag_state.top()) << '>';
                tag_state.pop();
        }
 
@@ -2697,10 +2806,10 @@ void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
        string top = top_element;
        top += " lang=\"";
        top += params.language->code();
-       top += "\"";
+       top += '"';
 
        if (!params.options.empty()) {
-               top += " ";
+               top += ' ';
                top += params.options;
        }
        sgml::openTag(ofs, 0, false, top);
@@ -2768,8 +2877,7 @@ void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
                case LATEX_COMMAND:
                        if (depth != 0)
                                sgmlError(par, 0,
-                                         _("Error : Wrong depth for "
-                                           "LatexType Command.\n"));
+                                         _("Error: Wrong depth for LatexType Command.\n"));
 
                        command_name = style->latexname();
 
@@ -2813,14 +2921,14 @@ void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
                                if (lyx_code == Inset::LABEL_CODE) {
                                        command_name += " id=\"";
                                        command_name += (static_cast<InsetCommand *>(inset))->getContents();
-                                       command_name += "\"";
+                                       command_name += '"';
                                        desc_on = 3;
                                }
                        }
 
                        sgml::openTag(ofs, depth + command_depth, false, command_name);
 
-                       item_name = c_params.empty()?"title":c_params;
+                       item_name = c_params.empty() ? "title" : c_params;
                        sgml::openTag(ofs, depth + 1 + command_depth, false, item_name);
                        break;
 
@@ -2896,7 +3004,7 @@ void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
                        break;
                case LATEX_ITEM_ENVIRONMENT:
                        if (desc_on == 1) break;
-                       end_tag= "para";
+                       end_tag = "para";
                        sgml::closeTag(ofs, depth + 1 + command_depth, false, end_tag);
                        break;
                case LATEX_PARAGRAPH:
@@ -3081,6 +3189,11 @@ void Buffer::validate(LaTeXFeatures & features) const
 {
        LyXTextClass const & tclass = params.getLyXTextClass();
 
+       if (params.tracking_changes) {
+               features.require("dvipost");
+               features.require("color");
+       }
        // AMS Style is at document level
        if (params.use_amsmath || tclass.provides(LyXTextClass::amsmath))
                features.require("amsmath");
@@ -3189,34 +3302,16 @@ vector<pair<string, string> > const Buffer::getBibkeyList() const
 
 bool Buffer::isDepClean(string const & name) const
 {
-       DEPCLEAN * item = dep_clean;
-       while (item && item->master != name)
-               item = item->next;
-       if (!item) return true;
-       return item->clean;
+       DepClean::const_iterator it = dep_clean_.find(name);
+       if (it == dep_clean_.end())
+               return true;
+       return it->second;
 }
 
 
 void Buffer::markDepClean(string const & name)
 {
-       if (!dep_clean) {
-               dep_clean = new DEPCLEAN;
-               dep_clean->clean = true;
-               dep_clean->master = name;
-               dep_clean->next = 0;
-       } else {
-               DEPCLEAN * item = dep_clean;
-               while (item && item->master != name)
-                       item = item->next;
-               if (item) {
-                       item->clean = true;
-               } else {
-                       item = new DEPCLEAN;
-                       item->clean = true;
-                       item->master = name;
-                       item->next = 0;
-               }
-       }
+       dep_clean_[name] = true;
 }
 
 
@@ -3423,10 +3518,12 @@ void Buffer::markDirty()
                updateTitles();
        }
        bak_clean = false;
-       DEPCLEAN * tmp = dep_clean;
-       while (tmp) {
-               tmp->clean = false;
-               tmp = tmp->next;
+
+       DepClean::iterator it = dep_clean_.begin();
+       DepClean::const_iterator const end = dep_clean_.end();
+
+       for (; it != end; ++it) {
+               it->second = false;
        }
 }