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.

Reply via email to