]> git.lyx.org Git - lyx.git/blobdiff - src/insets/InsetCitation.cpp
Cocoa based Qt-4.6 needs to paint every character separately to match metrics computa...
[lyx.git] / src / insets / InsetCitation.cpp
index 5921e7f1305b916ea2eaaf8eea77692bf81668ba..6f9035c5b9f4606d07f8323428d7beb0587cbef7 100644 (file)
 
 #include "InsetCitation.h"
 
+#include "BiblioInfo.h"
 #include "Buffer.h"
 #include "buffer_funcs.h"
 #include "BufferParams.h"
 #include "BufferView.h"
 #include "DispatchResult.h"
+#include "FuncCode.h"
 #include "FuncRequest.h"
 #include "LaTeXFeatures.h"
 #include "output_xhtml.h"
@@ -41,7 +43,7 @@ ParamInfo InsetCitation::param_info_;
 
 
 InsetCitation::InsetCitation(Buffer * buf, InsetCommandParams const & p)
-       : InsetCommand(buf, p, "citation")
+       : InsetCommand(buf, p)
 {}
 
 
@@ -99,6 +101,14 @@ bool InsetCitation::isCompatibleCommand(string const & cmd)
 }
 
 
+void InsetCitation::doDispatch(Cursor & cur, FuncRequest & cmd)
+{
+       if (cmd.action() == LFUN_INSET_MODIFY)
+               cache.recalculate = true;
+       InsetCommand::doDispatch(cur, cmd);
+}
+
+
 docstring InsetCitation::toolTip(BufferView const & bv, int, int) const
 {
        Buffer const & buf = bv.buffer();
@@ -119,7 +129,7 @@ docstring InsetCitation::toolTip(BufferView const & bv, int, int) const
        vector<docstring>::const_iterator en = keys.end();
        docstring tip;
        for (; it != en; ++it) {
-               docstring const key_info = bi.getInfo(*it);
+               docstring const key_info = bi.getInfo(*it, buffer());
                if (key_info.empty())
                        continue;
                if (!tip.empty())
@@ -199,22 +209,34 @@ string asValidLatexCommand(string const & input, CiteEngine const engine)
        return output;
 }
 
+
+inline docstring wrapCitation(docstring const & key, 
+               docstring const & content, bool for_xhtml)
+{
+       if (!for_xhtml)
+               return content;
+       // we have to do the escaping here, because we will ultimately
+       // write this as a raw string, so as not to escape the tags.
+       return "<a href='#" + key + "'>" +
+                       html::htmlize(content, XHTMLStream::ESCAPE_ALL) + "</a>";
+}
+
 } // anonymous namespace
 
-docstring InsetCitation::generateLabel() const
+docstring InsetCitation::generateLabel(bool for_xhtml) const
 {
        docstring label;
-       label = complexLabel();
+       label = complexLabel(for_xhtml);
 
        // Fallback to fail-safe
        if (label.empty())
-               label = basicLabel();
+               label = basicLabel(for_xhtml);
 
        return label;
 }
 
 
-docstring InsetCitation::complexLabel() const
+docstring InsetCitation::complexLabel(bool for_xhtml) const
 {
        Buffer const & buf = buffer();
        // Only start the process off after the buffer is loaded from file.
@@ -300,83 +322,90 @@ docstring InsetCitation::complexLabel() const
        for (; it != end; ++it) {
                // get the bibdata corresponding to the key
                docstring const author = biblist.getAbbreviatedAuthor(*it);
-               docstring const year = biblist.getYear(*it);
+               docstring const year = biblist.getYear(*it, for_xhtml);
+               docstring const citenum = for_xhtml ? biblist.getCiteNumber(*it) : *it;
 
-               // Something isn't right. Fail safely.
                if (author.empty() || year.empty())
+                       // We can't construct a "complex" label without that info.
+                       // So fail safely.
                        return docstring();
 
                // authors1/<before>;  ... ;
                //  authors_last, <after>
                if (cite_type == "cite") {
                        if (engine == ENGINE_BASIC) {
-                               label += *it + sep_str;
+                               label += wrapCitation(*it, citenum, for_xhtml) + sep_str;
                        } else if (engine == ENGINE_JURABIB) {
                                if (it == keys.begin())
-                                       label += author + before_str + sep_str;
+                                       label += wrapCitation(*it, author, for_xhtml) + before_str + sep_str;
                                else
-                                       label += author + sep_str;
+                                       label += wrapCitation(*it, author, for_xhtml) + sep_str;
                        }
-
+               } 
                // nocite
-               else if (cite_type == "nocite") {
+               else if (cite_type == "nocite") {
                        label += *it + sep_str;
-
+               } 
                // (authors1 (<before> year);  ... ;
                //  authors_last (<before> year, <after>)
-               else if (cite_type == "citet") {
+               else if (cite_type == "citet") {
                        switch (engine) {
                        case ENGINE_NATBIB_AUTHORYEAR:
                                label += author + op_str + before_str +
-                                       year + cp + sep_str;
+                                       wrapCitation(*it, year, for_xhtml) + cp + sep_str;
                                break;
                        case ENGINE_NATBIB_NUMERICAL:
-                               label += author + op_str + before_str + '#' + *it + cp + sep_str;
+                               label += author + op_str + before_str + 
+                                       wrapCitation(*it, citenum, for_xhtml) + cp + sep_str;
                                break;
                        case ENGINE_JURABIB:
                                label += before_str + author + op_str +
-                                       year + cp + sep_str;
+                                       wrapCitation(*it, year, for_xhtml) + cp + sep_str;
                                break;
                        case ENGINE_BASIC:
                                break;
                        }
-
-               // author, year; author, year; ...
-               else if (cite_type == "citep" ||
+               } 
+               // author, year; author, year; ...      
+               else if (cite_type == "citep" ||
                           cite_type == "citealp") {
                        if (engine == ENGINE_NATBIB_NUMERICAL) {
-                               label += *it + sep_str;
+                               label += wrapCitation(*it, citenum, for_xhtml) + sep_str;
                        } else {
-                               label += author + ", " + year + sep_str;
+                               label += wrapCitation(*it, author + ", " + year, for_xhtml) + sep_str;
                        }
 
+               } 
                // (authors1 <before> year;
                //  authors_last <before> year, <after>)
-               else if (cite_type == "citealt") {
+               else if (cite_type == "citealt") {
                        switch (engine) {
                        case ENGINE_NATBIB_AUTHORYEAR:
                                label += author + ' ' + before_str +
-                                       year + sep_str;
+                                       wrapCitation(*it, year, for_xhtml) + sep_str;
                                break;
                        case ENGINE_NATBIB_NUMERICAL:
-                               label += author + ' ' + before_str + '#' + *it + sep_str;
+                               label += author + ' ' + before_str + '#' + 
+                                       wrapCitation(*it, citenum, for_xhtml) + sep_str;
                                break;
                        case ENGINE_JURABIB:
-                               label += before_str + author + ' ' +
-                                       year + sep_str;
+                               label += before_str + 
+                                       wrapCitation(*it, author + ' ' + year, for_xhtml) + sep_str;
                                break;
                        case ENGINE_BASIC:
                                break;
                        }
 
+               
+               } 
                // author; author; ...
-               else if (cite_type == "citeauthor") {
-                       label += author + sep_str;
-
+               else if (cite_type == "citeauthor") {
+                       label += wrapCitation(*it, author, for_xhtml) + sep_str;
+               }
                // year; year; ...
-               else if (cite_type == "citeyear" ||
+               else if (cite_type == "citeyear" ||
                           cite_type == "citeyearpar") {
-                       label += year + sep_str;
+                       label += wrapCitation(*it, year, for_xhtml) + sep_str;
                }
        }
        label = rtrim(rtrim(label), sep);
@@ -409,22 +438,21 @@ docstring InsetCitation::complexLabel() const
 }
 
 
-docstring InsetCitation::basicLabel() const
+docstring InsetCitation::basicLabel(bool for_xhtml) const
 {
        docstring keys = getParam("key");
        docstring label;
 
-       if (contains(keys, ',')) {
-               // Final comma allows while loop to cover all keys
-               keys = ltrim(split(keys, label, ',')) + ',';
-               while (contains(keys, ',')) {
-                       docstring key;
-                       keys = ltrim(split(keys, key, ','));
-                       label += ", " + key;
-               }
-       } else {
-               label = keys;
-       }
+       docstring key;
+       do {
+               // if there is no comma, then everything goes into key
+               // and keys will be empty.
+               keys = trim(split(keys, key, ','));
+               key = trim(key);
+               if (!label.empty())
+                       label += ", ";
+               label += wrapCitation(key, key, for_xhtml);
+       } while (!keys.empty());
 
        docstring const & after = getParam("after");
        if (!after.empty())
@@ -439,34 +467,37 @@ docstring InsetCitation::screenLabel() const
 }
 
 
-void InsetCitation::updateLabels(ParIterator const &, bool)
+void InsetCitation::updateBuffer(ParIterator const &, UpdateType)
 {
-       CiteEngine const engine = buffer().params().citeEngine();
-       if (cache.params == params() && cache.engine == engine)
+       if (!cache.recalculate && buffer().citeLabelsValid())
                return;
 
-       // The label has changed, so we have to re-create it.
+       // The label may have changed, so we have to re-create it.
        docstring const glabel = generateLabel();
 
        unsigned int const maxLabelChars = 45;
 
        docstring label = glabel;
        if (label.size() > maxLabelChars) {
-               label.erase(maxLabelChars-3);
+               label.erase(maxLabelChars - 3);
                label += "...";
        }
 
-       cache.engine  = engine;
-       cache.params = params();
+       cache.recalculate = false;
        cache.generated_label = glabel;
        cache.screen_label = label;
 }
 
 
-void InsetCitation::addToToc(DocIterator const & cpit)
+void InsetCitation::addToToc(DocIterator const & cpit) const
 {
+       // NOTE
+       // XHTML output uses the TOC to collect the citations
+       // from the document. So if this gets changed, then we
+       // will need to change how the citations are collected.
+       docstring const tocitem = getParam("key");
        Toc & toc = buffer().tocBackend().toc("citation");
-       toc.push_back(TocItem(cpit, 0, getParam("key")));
+       toc.push_back(TocItem(cpit, 0, tocitem));
 }
 
 
@@ -510,63 +541,33 @@ docstring InsetCitation::xhtml(XHTMLStream & xs, OutputParams const &) const
        if (cmd == "nocite")
                return docstring();
 
-       BiblioInfo const & bi = buffer().masterBibInfo();
-       docstring const & key_list = getParam("key");
-       if (key_list.empty())
-               return docstring();
+       // have to output this raw, because generateLabel() will include tags
+       xs << XHTMLStream::ESCAPE_NONE << generateLabel(true);
 
-       // FIXME We should do a better job outputing different things for the
-       // different citation styles.   For now, we use square brackets for every
-       // case.
-       xs << "[";
-       docstring const & before = getParam("before");
-       if (!before.empty())
-               xs << before << " ";
-
-       vector<docstring> const keys = getVectorFromString(key_list);
-       vector<docstring>::const_iterator it = keys.begin();
-       vector<docstring>::const_iterator const en = keys.end();
-       bool first = true;
-       for (; it != en; ++it) {
-               BiblioInfo::const_iterator const bt = bi.find(*it);
-               if (bt == bi.end())
-                       continue;
-               BibTeXInfo const & bibinfo = bt->second;
-               if (!first) {
-                       xs << ", ";
-                       first = false;
-               }
-               docstring citekey = bibinfo.citeKey();
-               if (citekey.empty()) {
-                       citekey = bibinfo.label();
-                       if (citekey.empty())
-                               citekey = *it;
-               }
-               string const attr = "href='#" + to_utf8(*it) + "'";
-               xs << StartTag("a", attr) << citekey << EndTag("a");
-       }
-
-       docstring const & after = getParam("after");
-       if (!after.empty())
-               xs << ", " << after;
-       xs << "]";
        return docstring();
 }
 
 
-void InsetCitation::tocString(odocstream & os) const
+void InsetCitation::toString(odocstream & os) const
 {
        plaintext(os, OutputParams(0));
 }
 
 
+void InsetCitation::forToc(docstring & os, size_t) const
+{
+       os += screenLabel();
+}
+
+
 // Have to overwrite the default InsetCommand method in order to check that
 // the \cite command is valid. Eg, the user has natbib enabled, inputs some
 // citations and then changes his mind, turning natbib support off. The output
 // should revert to \cite[]{}
-int InsetCitation::latex(odocstream & os, OutputParams const & runparams) const
+void InsetCitation::latex(otexstream & os, OutputParams const & runparams) const
 {
        CiteEngine cite_engine = buffer().params().citeEngine();
+       BiblioInfo const & bi = buffer().masterBibInfo();
        // FIXME UNICODE
        docstring const cite_str = from_utf8(
                asValidLatexCommand(getCmdName(), cite_engine));
@@ -583,12 +584,14 @@ int InsetCitation::latex(odocstream & os, OutputParams const & runparams) const
        else if (!after.empty())
                os << '[' << after << ']';
 
-       os << '{' << cleanupWhitespace(getParam("key")) << '}';
+       if (!bi.isBibtex(getParam("key")))
+               // escape chars with bibitems
+               os << '{' << escape(cleanupWhitespace(getParam("key"))) << '}';
+       else
+               os << '{' << cleanupWhitespace(getParam("key")) << '}';
 
        if (runparams.inulemcmd)
                os << "}";
-
-       return 0;
 }
 
 
@@ -608,7 +611,7 @@ void InsetCitation::validate(LaTeXFeatures & features) const
 }
 
 
-docstring InsetCitation::contextMenu(BufferView const &, int, int) const
+docstring InsetCitation::contextMenuName() const
 {
        return from_ascii("context-citation");
 }