Georg Baum wrote:

> All ArgumentProxy instances are contained in MathMacro::expanded_, which
> means that there is always a unique parent-child like relationship between
> MathMacro and ArgumentProxy. Therefore, a single place exists where
> ArgumentProxy::mathMacro_ can become invalid, and where it could be
> updated: Whenever a MathMacro object is copied. Therefore we can simply
> create a user defined copy constructor and assignment operator for
> MathMacro, and update all ArgumentProxy::mathMacro_ references after
> copying the macro. That's it! With this simple fix we do not require
> anymore the additional property of RandomAccessList that resizing never
> copies the elemnts. Furthermore, the fix also helps in cases where the
> complete MathMacro::expanded_ vector is copied, which would not be fixed
> by converting it to a RandomAccessList only.
> 
> The drawback of that approach is that it is easy to forget to update the
> copy constructor and assignment operator when adding new members.
> Therefore I'd like to use a pimpl, so that the pimpl copies can be
> compiler generated, and the manual operators do not need to be touched in
> the future. This looks like the attached lengthy patch. If I don't get
> complaints I'd like to put this into master, and for 2.1 I'd like to do
> the same, but in order to minimize the changes without the pimpl, i.e.
> with a manual copy constructor and assignment operator.

This is now in master.

Richard, for 2.1 I propose the attached solution without pimpl. I do not 
want to change too much code, and I don't expect any new memeber variables 
in 2.1 anymore. OK?


Georg
diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp
index bb855b2..06cfd5c 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -52,35 +52,37 @@ namespace lyx {
 class ArgumentProxy : public InsetMath {
 public:
 	///
-	ArgumentProxy(MathMacro & mathMacro, size_t idx)
+	ArgumentProxy(MathMacro * mathMacro, size_t idx)
 		: mathMacro_(mathMacro), idx_(idx) {}
 	///
-	ArgumentProxy(MathMacro & mathMacro, size_t idx, docstring const & def)
+	ArgumentProxy(MathMacro * mathMacro, size_t idx, docstring const & def)
 		: mathMacro_(mathMacro), idx_(idx)
 	{
 			asArray(def, def_);
 	}
 	///
+	void setOwner(MathMacro * mathMacro) { mathMacro_ = mathMacro; }
+	///
 	InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
 	///
 	void metrics(MetricsInfo & mi, Dimension & dim) const {
-		mathMacro_.macro()->unlock();
-		mathMacro_.cell(idx_).metrics(mi, dim);
+		mathMacro_->macro()->unlock();
+		mathMacro_->cell(idx_).metrics(mi, dim);
 
-		if (!mathMacro_.editMetrics(mi.base.bv)
-		    && mathMacro_.cell(idx_).empty())
+		if (!mathMacro_->editMetrics(mi.base.bv)
+		    && mathMacro_->cell(idx_).empty())
 			def_.metrics(mi, dim);
 
-		mathMacro_.macro()->lock();
+		mathMacro_->macro()->lock();
 	}
 	// FIXME Other external things need similar treatment.
 	///
-	void mathmlize(MathStream & ms) const { ms << mathMacro_.cell(idx_); }
+	void mathmlize(MathStream & ms) const { ms << mathMacro_->cell(idx_); }
 	///
-	void htmlize(HtmlStream & ms) const { ms << mathMacro_.cell(idx_); }
+	void htmlize(HtmlStream & ms) const { ms << mathMacro_->cell(idx_); }
 	///
 	void draw(PainterInfo & pi, int x, int y) const {
-		if (mathMacro_.editMetrics(pi.base.bv)) {
+		if (mathMacro_->editMetrics(pi.base.bv)) {
 			// The only way a ArgumentProxy can appear is in a cell of the
 			// MathMacro. Moreover the cells are only drawn in the DISPLAY_FOLDED
 			// mode and then, if the macro is edited the monochrome
@@ -90,22 +92,22 @@ public:
 			// here (and the assert triggers in pain.leaveMonochromeMode())
 			// it's a bug.
 			pi.pain.leaveMonochromeMode();
-			mathMacro_.cell(idx_).draw(pi, x, y);
+			mathMacro_->cell(idx_).draw(pi, x, y);
 			pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
-		} else if (mathMacro_.cell(idx_).empty()) {
-			mathMacro_.cell(idx_).setXY(*pi.base.bv, x, y);
+		} else if (mathMacro_->cell(idx_).empty()) {
+			mathMacro_->cell(idx_).setXY(*pi.base.bv, x, y);
 			def_.draw(pi, x, y);
 		} else
-			mathMacro_.cell(idx_).draw(pi, x, y);
+			mathMacro_->cell(idx_).draw(pi, x, y);
 	}
 	///
 	size_t idx() const { return idx_; }
 	///
 	int kerning(BufferView const * bv) const
 	{
-		if (mathMacro_.editMetrics(bv)
-		    || !mathMacro_.cell(idx_).empty())
-			return mathMacro_.cell(idx_).kerning(bv);
+		if (mathMacro_->editMetrics(bv)
+		    || !mathMacro_->cell(idx_).empty())
+			return mathMacro_->cell(idx_).kerning(bv);
 		else
 			return def_.kerning(bv);
 	}
@@ -117,7 +119,7 @@ private:
 		return new ArgumentProxy(*this);
 	}
 	///
-	MathMacro & mathMacro_;
+	MathMacro * mathMacro_;
 	///
 	size_t idx_;
 	///
@@ -133,6 +135,50 @@ MathMacro::MathMacro(Buffer * buf, docstring const & name)
 {}
 
 
+MathMacro::MathMacro(MathMacro const & that)
+	: InsetMathNest(that), expanded_(that.buffer_)
+{
+	assign(that);
+}
+
+
+MathMacro & MathMacro::operator=(MathMacro const & that)
+{
+	if (&that == this)
+		return *this;
+	InsetMathNest::operator=(that);
+	assign(that);
+	return *this;
+}
+
+
+void MathMacro::assign(MathMacro const & that)
+{
+	name_ = that.name_;
+	displayMode_ = that.displayMode_;
+	expanded_ = that.expanded_;
+	definition_ = that.definition_;
+	attachedArgsNum_ = that.attachedArgsNum_;
+	optionals_ = that.optionals_;
+	nextFoldMode_ = that.nextFoldMode_;
+	macroBackup_ = that.macroBackup_;
+	macro_ = that.macro_;
+	editing_ = that.editing_;
+	requires_ = that.requires_;
+	needsUpdate_ = that.needsUpdate_;
+	isUpdating_ = that.isUpdating_;
+	appetite_ = that.appetite_;
+	// Update the pointers to our owner of all expanded macros.
+	// This needs to be called every time a copy of the owner is created
+	// (bug 9418).
+	for (size_t i = 0; i < expanded_.cell(0).size(); ++i) {
+		ArgumentProxy * p = dynamic_cast<ArgumentProxy *>(expanded_.cell(0)[i].nucleus());
+		if (p)
+			p->setOwner(this);
+	}
+}
+
+
 Inset * MathMacro::clone() const
 {
 	MathMacro * copy = new MathMacro(*this);
@@ -358,9 +404,9 @@ void MathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc,
 	for (size_t i = 0; i < nargs(); ++i) {
 		ArgumentProxy * proxy;
 		if (i < defaults.size())
-			proxy = new ArgumentProxy(*this, i, defaults[i]);
+			proxy = new ArgumentProxy(this, i, defaults[i]);
 		else
-			proxy = new ArgumentProxy(*this, i);
+			proxy = new ArgumentProxy(this, i);
 		values[i].insert(0, MathAtom(proxy));
 	}
 	// expanding macro with the values
diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index b2d7553..239495e 100644
--- a/src/mathed/MathMacro.h
+++ b/src/mathed/MathMacro.h
@@ -28,6 +28,10 @@ public:
 	/// A macro can be built from an existing template
 	MathMacro(Buffer * buf, docstring const & name);
 	///
+	MathMacro(MathMacro const &);
+	///
+	MathMacro & operator=(MathMacro const &);
+	///
 	virtual MathMacro * asMacro() { return this; }
 	///
 	virtual MathMacro const * asMacro() const { return this; }
@@ -156,6 +160,8 @@ private:
 	virtual Inset * clone() const;
 	///
 	bool editMode(BufferView const * bv) const;
+	/// Copy all members (except base class members)
+	void assign(MathMacro const &);
 	
 	/// name of macro
 	docstring name_;
diff --git a/status.21x b/status.21x
index 0bb056c..378e27d 100644
--- a/status.21x
+++ b/status.21x
@@ -122,6 +122,8 @@ What's new
 
 - Fix insertion of spaces in macro definitions (bug 9432).
 
+- Fix crash when copying macros with arguments (bug 9418).
+
 - Fix name and hint of figure captions in documents using the class aastex.
 
 - Fix output encoding information for non-TeX fonts XeTeX/LuaTeX on preview

Reply via email to