http://bugzilla.lyx.org/show_bug.cgi?id=1684
The attached patch (against 1.4svn) fixes the "duplicate" part, i.e. it checks whenever the user inserts a label/bibitem if it already exists. If so, it pops up a warning an renames the label to something unique ("foo" -> "foo-1" etc.). It also works for cut and paste (where it also renames the associated refs/citations in the pasted chunk). Comments appreciated. Jürgen
Index: src/insets/insetlabel.C =================================================================== --- src/insets/insetlabel.C (Revision 15282) +++ src/insets/insetlabel.C (Arbeitskopie) @@ -69,6 +69,10 @@ void InsetLabel::doDispatch(LCursor & cu cur.noUpdate(); break; } + string newlabel = cur.bv().buffer()->validateLabel( + p.getContents(), InsetBase::LABEL_CODE); + if (p.getContents() != newlabel) + p.setContents(newlabel); if (p.getContents() != params().getContents()) cur.bv().buffer()->changeRefsIfUnique(params().getContents(), p.getContents(), InsetBase::REF_CODE); Index: src/insets/insetbibitem.C =================================================================== --- src/insets/insetbibitem.C (Revision 15282) +++ src/insets/insetbibitem.C (Arbeitskopie) @@ -65,6 +65,10 @@ void InsetBibitem::doDispatch(LCursor & cur.noUpdate(); break; } + string newlabel = cur.bv().buffer()->validateLabel( + p.getContents(), InsetBase::BIBITEM_CODE); + if (p.getContents() != newlabel) + p.setContents(newlabel); if (p.getContents() != params().getContents()) cur.bv().buffer()->changeRefsIfUnique(params().getContents(), p.getContents(), InsetBase::CITE_CODE); Index: src/mathed/math_hullinset.C =================================================================== --- src/mathed/math_hullinset.C (Revision 15282) +++ src/mathed/math_hullinset.C (Arbeitskopie) @@ -1077,6 +1077,7 @@ void MathHullInset::doDispatch(LCursor & if (!str.empty()) numbered(r, true); string old = label(r); + str = cur.bv().buffer()->validateLabel(str, InsetBase::LABEL_CODE); if (str != old) { cur.bv().buffer()->changeRefsIfUnique(old, str, InsetBase::REF_CODE); label(r, str); Index: src/factory.C =================================================================== --- src/factory.C (Revision 15282) +++ src/factory.C (Arbeitskopie) @@ -208,6 +208,11 @@ InsetBase * createInset(BufferView * bv, InsetCommandParams icp; InsetCommandMailer::string2params(name, cmd.argument, icp); + // check for duplicate labels + string newlabel = bv->buffer()->validateLabel( + icp.getContents(), InsetBase::BIBITEM_CODE); + if (icp.getContents() != newlabel) + icp.setContents(newlabel); return new InsetBibitem(icp); } else if (name == "bibtex") { @@ -260,6 +265,11 @@ InsetBase * createInset(BufferView * bv, InsetCommandParams icp; InsetCommandMailer::string2params(name, cmd.argument, icp); + // check for duplicate labels + string newlabel = bv->buffer()->validateLabel( + icp.getContents(), InsetBase::LABEL_CODE); + if (icp.getContents() != newlabel) + icp.setContents(newlabel); return new InsetLabel(icp); } else if (name == "ref") { Index: src/buffer.C =================================================================== --- src/buffer.C (Revision 15282) +++ src/buffer.C (Arbeitskopie) @@ -122,6 +122,7 @@ namespace os = lyx::support::os; namespace fs = boost::filesystem; using std::endl; +using std::find; using std::for_each; using std::make_pair; @@ -1612,23 +1613,54 @@ void Buffer::saveCursor(StableDocIterato } -void Buffer::changeRefsIfUnique(string const & from, string const & to, InsetBase::Code code) +vector<string> const Buffer::getLabelsOrCitations(InsetBase::Code code) const { - BOOST_ASSERT(code == InsetBase::CITE_CODE || code == InsetBase::REF_CODE); - // Check if the label 'from' appears more than once vector<string> labels; - if (code == InsetBase::CITE_CODE) { + if (code == InsetBase::BIBITEM_CODE || code == InsetBase::CITE_CODE) { vector<pair<string, string> > keys; fillWithBibKeys(keys); vector<pair<string, string> >::const_iterator bit = keys.begin(); vector<pair<string, string> >::const_iterator bend = keys.end(); - for (; bit != bend; ++bit) labels.push_back(bit->first); } else getLabelList(labels); + return labels; +} + + +string const Buffer::validateLabel(string const & label, InsetBase::Code code) const +{ + BOOST_ASSERT(code == InsetBase::BIBITEM_CODE || code == InsetBase::LABEL_CODE); + + vector<string> labels = getLabelsOrCitations(code); + + if (find(labels.begin(), labels.end(), label) != labels.end()) { + // generate unique label + int i = 1; + string newlabel = label + "-" + convert<string>(i); + while (find(labels.begin(), labels.end(), newlabel) != labels.end()) { + ++i; + newlabel = label + "-" + convert<string>(i); + } + Alert::warning(_("Label names must be unique"), + bformat(_("The label %1$s already exists.\n" + "The name has been changed to %2$s."), + label, newlabel)); + return newlabel; + } + return label; +} + + +void Buffer::changeRefsIfUnique(string const & from, string const & to, InsetBase::Code code) +{ + BOOST_ASSERT(code == InsetBase::CITE_CODE || code == InsetBase::REF_CODE); + + // Check if the label 'from' appears more than once + vector<string> labels = getLabelsOrCitations(code); if (lyx::count(labels.begin(), labels.end(), from) > 1) return; Index: src/CutAndPaste.C =================================================================== --- src/CutAndPaste.C (Revision 15282) +++ src/CutAndPaste.C (Arbeitskopie) @@ -37,6 +37,7 @@ #include "undo.h" #include "insets/insetcharstyle.h" +#include "insets/insetcommand.h" #include "insets/insettabular.h" #include "mathed/math_data.h" @@ -45,6 +46,7 @@ #include "support/lstrings.h" +#include <algorithm> #include <boost/tuple/tuple.hpp> using lyx::pos_type; @@ -52,11 +54,15 @@ using lyx::pit_type; using lyx::textclass_type; using lyx::support::bformat; +using lyx::support::getStringFromVector; +using lyx::support::getVectorFromString; +using lyx::support::tokenPos; using std::endl; using std::for_each; using std::make_pair; using std::pair; +using std::replace; using std::vector; using std::string; @@ -223,24 +229,58 @@ pasteSelectionHelper(LCursor & cur, Para // A couple of insets store buffer references so need updating. insertion.swap(in.paragraphs()); - ParIterator fpit = par_iterator_begin(in); - ParIterator fend = par_iterator_end(in); + InsetIterator const i_end = inset_iterator_end(in); - for (; fpit != fend; ++fpit) { - InsetList::iterator lit = fpit->insetlist.begin(); - InsetList::iterator eit = fpit->insetlist.end(); - - for (; lit != eit; ++lit) { - switch (lit->inset->lyxCode()) { - case InsetBase::TABULAR_CODE: { - InsetTabular * it = static_cast<InsetTabular*>(lit->inset); - it->buffer(&buffer); - break; - } + for (InsetIterator it = inset_iterator_begin(in); it != i_end; ++it) { + + switch (it->lyxCode()) { - default: - break; // nothing + case InsetBase::TABULAR_CODE: { + InsetTabular & tabular = static_cast<InsetTabular &>(*it); + tabular.buffer(&buffer); + break; + } + case InsetBase::LABEL_CODE: { + // check for duplicates + InsetCommand & lab = static_cast<InsetCommand &>(*it); + string const oldcontent = lab.getContents(); + string const newcontent = cur.bv().buffer()->validateLabel( + oldcontent, InsetBase::LABEL_CODE); + if (newcontent != oldcontent) { + // replace with unique label + lab.replaceContents(oldcontent, newcontent); + // adapt the references + for (InsetIterator itt = inset_iterator_begin(in); itt != i_end; ++itt) { + if (itt->lyxCode() == InsetBase::REF_CODE) { + InsetCommand & ref = dynamic_cast<InsetCommand &>(*itt); + ref.replaceContents(oldcontent, newcontent); + } + } } + break; + } + case InsetBase::BIBITEM_CODE: { + // check for duplicates + InsetCommand & bib = static_cast<InsetCommand &>(*it); + string const oldcontent = bib.getContents(); + string const newcontent = cur.bv().buffer()->validateLabel( + oldcontent, InsetBase::BIBITEM_CODE); + if (newcontent != oldcontent) { + // replace with unique label + bib.replaceContents(oldcontent, newcontent); + // adapt the references + for (InsetIterator itt = inset_iterator_begin(in); itt != i_end; ++itt) { + if (itt->lyxCode() == InsetBase::CITE_CODE) { + InsetCommand & cite = dynamic_cast<InsetCommand &>(*itt); + cite.replaceContents(oldcontent, newcontent); + } + } + } + break; + } + + default: + break; // nothing } } insertion.swap(in.paragraphs()); Index: src/buffer.h =================================================================== --- src/buffer.h (Revision 15282) +++ src/buffer.h (Arbeitskopie) @@ -342,6 +342,9 @@ public: StableDocIterator getCursor() const { return cursor_; } /// StableDocIterator getAnchor() const { return anchor_; } + /// check if \param label ist valid (unique) and return + /// a valid one, if not. + std::string const validateLabel(std::string const & label, InsetBase::Code code) const; /// void changeRefsIfUnique(std::string const & from, std::string const & to, InsetBase::Code code); @@ -353,6 +356,9 @@ private: bool do_writeFile(std::ostream & ofs) const; + /// A helper function for validate Label and ChangeRefsIfUnique + std::vector<std::string> const getLabelsOrCitations(InsetBase::Code code) const; + /// Use the Pimpl idiom to hide the internals. class Impl; /// The pointer never changes although *pimpl_'s contents may.