1 // -*- C++ -*-
2 
3 /*
4  * GChemPaint bonds plugin
5  * chaintool.cc
6  *
7  * Copyright (C) 2006-2011 Jean Bréfort <jean.brefort@normalesup.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 3 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
22  * USA
23  */
24 
25 #include "config.h"
26 #include "chaintool.h"
27 #include <gcp/application.h>
28 #include <gcp/atom.h>
29 #include <gcp/bond.h>
30 #include <gcp/document.h>
31 #include <gcp/molecule.h>
32 #include <gcp/settings.h>
33 #include <gcp/theme.h>
34 #include <gcp/view.h>
35 #include <gccv/canvas.h>
36 #include <gccv/group.h>
37 #include <gccv/line.h>
38 #include <gcugtk/ui-builder.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <glib/gi18n-lib.h>
41 #include <cmath>
42 #include <list>
43 
44 using namespace gcu;
45 using namespace std;
46 
gcpChainTool(gcp::Application * App)47 gcpChainTool::gcpChainTool (gcp::Application *App): gcp::Tool (App, "Chain")
48 {
49 	m_Length = 0; // < 2 is auto.
50 	m_Points = new gccv::Point[3];
51 	m_Atoms.resize (3);
52 	m_CurPoints = 3;
53 	m_AutoNb = true;
54 	m_AutoDir = false;
55 }
56 
~gcpChainTool()57 gcpChainTool::~gcpChainTool()
58 {
59 	delete [] m_Points;
60 }
61 
OnClicked()62 bool gcpChainTool::OnClicked()
63 {
64 	if (Element::GetMaxBonds (m_pApp->GetCurZ()) < 2)
65 		return false;
66 	m_dAngle = 0.;
67 	unsigned nb = (m_Length > 2)? m_Length + 1: 3;
68 	double a1, x, y;
69 	gcp::Document* pDoc = m_pView->GetDoc();
70 	m_BondLength = pDoc->GetBondLength ();
71 	if (nb != m_CurPoints) {
72 		m_CurPoints = nb;
73 		delete [] m_Points;
74 		m_Points = new gccv::Point[m_CurPoints];
75 		if (m_CurPoints > m_Atoms.size ())
76 			m_Atoms.resize (m_CurPoints);
77 	}
78 	m_Positive = (m_nState & GDK_LOCK_MASK && !(m_nState & GDK_MOD5_MASK)) ||
79 				(m_nState & GDK_MOD5_MASK && !(m_nState & GDK_LOCK_MASK));
80 	if (m_pObject) {
81 		if (m_pObject->GetType () != AtomType)
82 			return false;
83 		m_Atoms[0] = static_cast<gcp::Atom*> (m_pObject);
84 		if (!m_Atoms[0]->AcceptNewBonds (1))
85 			return false;
86 		nb = m_Atoms[0]->GetBondsNumber ();
87 		m_Atoms[0]->GetCoords(&m_x0, &m_y0, NULL);
88 		m_Points[0].x = x = m_x0 *= m_dZoomFactor;
89 		m_Points[0].y = y = m_y0 *= m_dZoomFactor;
90 		switch (nb) {
91 		case 1: {
92 				map<Atom*, Bond*>::iterator i;
93 				gcp::Bond* bond = (gcp::Bond*) ((Atom*) m_pObject)->GetFirstBond (i);
94 				m_RefAngle = m_dAngle = bond->GetAngle2D ((gcp::Atom*) m_pObject);
95 				m_dAngle += (m_Positive)? +150: -150;
96 				m_AutoDir = true;
97 				break;
98 			}
99 		case 2: {
100 				double a2;
101 				map<Atom*, Bond*>::iterator i;
102 				gcp::Bond* bond = (gcp::Bond*) ((Atom*) m_pObject)->GetFirstBond (i);
103 				a1 = bond->GetAngle2D ((gcp::Atom*) m_pObject);
104 				bond = (gcp::Bond*) ((Atom*) m_pObject)->GetNextBond (i);
105 				a2 = bond->GetAngle2D ((gcp::Atom*) m_pObject);
106 				m_dAngle = (a1 + a2) / 2.;
107 				a2 = fabs (a2 - m_dAngle);
108 				if (a2 < 90.)
109 					m_dAngle += 180.;
110 				if (m_dAngle > 360.)
111 					m_dAngle -= 360.;
112 				m_dAngle += (m_Positive)?
113 						90. - pDoc->GetBondAngle () / 2.:
114 						pDoc->GetBondAngle () / 2. - 90;
115 				break;
116 			}
117 		default:
118 			break;
119 		}
120 	} else {
121 		m_Atoms[0] = NULL;
122 		x = m_Points->x = m_x0;
123 		y = m_Points->y = m_y0;
124 		m_AutoDir = true;
125 	}
126 	FindAtoms ();
127 	m_Allowed = false;
128 	if (gcp::MergeAtoms && !(m_Allowed = CheckIfAllowed ()))
129 		return true; // true, since dragging the mouse might make things OK.
130 	char tmp[32];
131 	snprintf(tmp, sizeof(tmp) - 1, _("Bonds: %d, Orientation: %g"), m_CurPoints - 1, m_dAngle);
132 	m_pApp->SetStatusText(tmp);
133 	Draw ();
134 	m_dMeanLength = pDoc->GetBondLength () * sin (pDoc->GetBondAngle () / 360. * M_PI) * m_dZoomFactor;
135 	m_Allowed = true;
136 	return true;
137 }
138 
OnDrag()139 void gcpChainTool::OnDrag ()
140 {
141 	double x1 = 0., y1 = 0., x2; // initialize to make gcc happy
142 	unsigned nb;
143 	gcp::Document* pDoc = m_pView->GetDoc ();
144 	m_BondLength = pDoc->GetBondLength ();
145 	gccv::Canvas *canvas = m_pView->GetCanvas ();
146 	gccv::Item *item = canvas->GetItemAt (m_x, m_y);
147 	Object* pObject = NULL;
148 	if (item)
149 		pObject = dynamic_cast <Object *> (item->GetClient ());
150 	double dAngle = m_dAngle;
151 	gcp::Atom *pAtom = NULL;
152 	if (pObject) {
153 		if (pObject->GetType () == BondType)
154 			pAtom = (gcp::Atom*) pObject->GetAtomAt (m_x / m_dZoomFactor, m_y / m_dZoomFactor);
155 		else if (pObject->GetType () == FragmentType)
156 			pAtom = (gcp::Atom*) pObject->GetAtomAt (m_x1 / m_dZoomFactor, m_y1 / m_dZoomFactor);
157 		else if (pObject->GetType () == AtomType)
158 			pAtom = (gcp::Atom*) pObject;
159 	}
160 	m_Positive = (m_nState & GDK_LOCK_MASK && !(m_nState & GDK_MOD5_MASK)) ||
161 				(m_nState & GDK_MOD5_MASK && !(m_nState & GDK_LOCK_MASK));
162 	if (m_pObject && pAtom == m_pObject) {
163 		if (m_AutoDir) {
164 			m_dAngle = m_RefAngle + ((m_Positive)? +150: -150);
165 			pAtom = NULL;
166 		} else
167 			return;
168 	} else if (m_pObject || m_x != m_x0 || m_y != m_y0)
169 		m_AutoDir = false;
170 	// If m_Length has changed, adjust the points number
171 	if (m_Length > 1 && m_CurPoints != m_Length + 1) {
172 		m_CurPoints = m_Length + 1;
173 		delete [] m_Points;
174 		m_Points = new gccv::Point[m_CurPoints];
175 		if (m_CurPoints > m_Atoms.size ())
176 			m_Atoms.resize (m_CurPoints);
177 	}
178 	if (pAtom && gcp::MergeAtoms) {
179 		// in that case, end the chain there with the current number of bonds
180 		pAtom->GetCoords (&m_x, &m_y, NULL);
181 		m_x *= m_dZoomFactor;
182 		m_y *= m_dZoomFactor;
183 		m_x-= m_x0;
184 		m_y -= m_y0;
185 		x2 = sqrt (m_x * m_x + m_y * m_y);
186 		if (m_CurPoints % 2 == 0) {
187 			x1 = m_dMeanLength * (m_CurPoints - 1);
188 			y1 = pDoc->GetBondLength () * cos (pDoc->GetBondAngle () / 360. * M_PI) * m_dZoomFactor;
189 			m_dAngle = (atan2 (-m_y, m_x) - atan2((m_Positive)? -y1: y1, x1)) / M_PI * 180.;
190 			m_BondLength = pDoc->GetBondLength () * x2 / x1;
191 		} else {
192 			m_dAngle = atan2 (-m_y, m_x) / M_PI * 180.;
193 			m_BondLength = x2 / (m_CurPoints - 1) / sin (pDoc->GetBondAngle () / 360. * M_PI) / m_dZoomFactor;
194 		}
195 	} else if (!m_AutoDir) {
196 		m_x-= m_x0;
197 		m_y -= m_y0;
198 		if (m_x == 0) {
199 			if (m_y == 0)
200 				return;
201 			dAngle = (m_y < 0)? 90: 270;
202 		} else {
203 		// calculate the angle and the real distance
204 			dAngle = atan (-m_y/m_x) * 180 / M_PI;
205 			if (!(m_nState & GDK_CONTROL_MASK))
206 				dAngle = rint(dAngle / 5) * 5;
207 			if (isnan (dAngle))
208 				dAngle = m_dAngle;
209 			else if (m_x < 0.)
210 				dAngle += 180.;
211 		}
212 		m_dAngle = dAngle;
213 		// Calculate number of bonds if Shift key is not pressed and we do use
214 		// an automatic bonds number (otherwise, change the bonds lengths,
215 		//	but not their number
216 		dAngle = atan2 (-m_y, m_x) - m_dAngle * M_PI / 180.;
217 		x2 = sqrt ((m_x * m_x + m_y * m_y) * cos (dAngle));
218 		if (m_nState & GDK_SHIFT_MASK)
219 			m_BondLength = x2 / (m_CurPoints - 1) / sin (pDoc->GetBondAngle () / 360. * M_PI) / m_dZoomFactor;
220 		else if (m_Length < 2) {
221 			nb = 1 + (unsigned) rint (x2 / m_dMeanLength);
222 			if (nb < 3)
223 				nb = 3;
224 			if (nb != m_CurPoints) {
225 				m_CurPoints = nb;
226 				delete [] m_Points;
227 				m_Points = new gccv::Point[m_CurPoints];
228 				if (m_CurPoints > m_Atoms.size ())
229 					m_Atoms.resize (m_CurPoints);
230 			}
231 		}
232 	}
233 	m_Points->x = m_x0;
234 	m_Points->y = m_y0;
235 	FindAtoms ();
236 	if (gcp::MergeAtoms && !(m_Allowed = CheckIfAllowed ())) {
237 		if (m_Item) {
238 			delete m_Item;
239 			m_Item = NULL;
240 		}
241 		return;
242 	}
243 	char tmp[32];
244 	snprintf (tmp, sizeof (tmp) - 1, _("Bonds: %d, Orientation: %g"), m_CurPoints - 1, m_dAngle);
245 	m_pApp->SetStatusText(tmp);
246 	Draw ();
247 
248 }
249 
OnRelease()250 void gcpChainTool::OnRelease ()
251 {
252 	gcp::Document* pDoc = m_pView->GetDoc ();
253 	unsigned nb;
254 	gcp::Operation *pOp = NULL;
255 	Object *pObject;
256 	char const *Id;
257 	gcp::Molecule *pMol = NULL;
258 	gcp::Bond* pBond = NULL;
259 	m_pApp->ClearStatus ();
260 	m_AutoDir = false;
261 	if (m_Item) {
262 		delete m_Item;
263 		m_Item = NULL;
264 	} else
265 		return;
266 	if (!m_Allowed)
267 		return;
268 	// first save groups which need to be saved
269 	for (nb = 0; nb < m_CurPoints; nb++) {
270 		if (m_Atoms[nb]) {
271 			if (pMol == NULL) {
272 				pMol = dynamic_cast<gcp::Molecule *> (m_Atoms[nb]->GetMolecule ());
273 				pMol->Lock (true);
274 			}
275 			pObject = m_Atoms[nb]->GetGroup ();
276 			Id = pObject->GetId ();
277 			if (ModifiedObjects.find (Id) == ModifiedObjects.end ()) {
278 				if (!pOp)
279 					pOp = pDoc->GetNewOperation (gcp::GCP_MODIFY_OPERATION);
280 				pOp->AddObject (pObject);
281 				ModifiedObjects.insert (Id);
282 			}
283 		}
284 	}
285 	// now add new atoms and bonds
286 	for (nb = 0; nb < m_CurPoints; nb++) {
287 		if (!m_Atoms[nb]) {
288 			m_Atoms[nb] = new gcp::Atom (m_pApp->GetCurZ(),
289 				m_Points[nb].x / m_dZoomFactor,
290 				m_Points[nb].y / m_dZoomFactor,
291 				0);
292 			pDoc->AddAtom (m_Atoms[nb]);
293 		}
294 		// now add the bond. Atoms might be the same if the bonds are too short.
295 		if (nb > 0 && m_Atoms[nb] != m_Atoms[nb - 1]) {
296 			pBond = reinterpret_cast<gcp::Bond*> (m_Atoms[nb]->GetBond (m_Atoms[nb - 1]));
297 			if (!pBond) {
298 				pBond = new gcp::Bond (m_Atoms[nb - 1], m_Atoms[nb], 1);
299 				pDoc->AddBond (pBond);
300 			}
301 		}
302 	}
303 	pObject = pBond->GetGroup ();
304 	if (pOp) {
305 		ModifiedObjects.insert (pObject->GetId ());
306 		set<string>::iterator it, end = ModifiedObjects.end ();
307 		for (it = ModifiedObjects.begin (); it != end; it++) {
308 			pObject = pDoc->GetDescendant ((*it).c_str ());
309 			if (pObject)
310 				pOp->AddObject (pObject, 1);
311 		}
312 	} else {
313 		pOp = pDoc->GetNewOperation (gcp::GCP_ADD_OPERATION);
314 		pOp->AddObject (pObject);
315 	}
316 	pDoc->FinishOperation ();
317 	if (pMol) {
318 		pMol->Lock (false);
319 		pMol->EmitSignal (gcp::OnChangedSignal);
320 	}
321 	ModifiedObjects.clear ();
322 }
323 
on_length_changed(GtkSpinButton * btn,gcpChainTool * tool)324 static void on_length_changed (GtkSpinButton *btn, gcpChainTool *tool)
325 {
326 	tool->SetLength (gtk_spin_button_get_value (btn));
327 }
328 
on_angle_changed(GtkSpinButton * btn,gcpChainTool * tool)329 static void on_angle_changed (GtkSpinButton *btn, gcpChainTool *tool)
330 {
331 	tool->SetAngle (gtk_spin_button_get_value (btn));
332 }
333 
on_merge_toggled(GtkToggleButton * btn)334 static void on_merge_toggled (GtkToggleButton *btn)
335 {
336 	gcp::MergeAtoms = gtk_toggle_button_get_active (btn);
337 }
338 
on_number_toggled(GtkToggleButton * btn,gcpChainTool * tool)339 static void on_number_toggled (GtkToggleButton *btn, gcpChainTool *tool)
340 {
341 	bool active = gtk_toggle_button_get_active (btn);
342 	if (active)
343 		tool->SetChainLength (0);
344 	tool->SetAutoNumber (!gtk_toggle_button_get_active (btn));
345 }
346 
on_number_changed(GtkSpinButton * btn,gcpChainTool * tool)347 static void on_number_changed (GtkSpinButton *btn, gcpChainTool *tool)
348 {
349 	tool->SetChainLength (gtk_spin_button_get_value_as_int (btn));
350 }
351 
SetAngle(double angle)352 void gcpChainTool::SetAngle (double angle)
353 {
354 	m_pApp->GetActiveDocument ()->SetBondAngle (angle);
355 }
356 
SetLength(double length)357 void gcpChainTool::SetLength (double length)
358 {
359 	m_pApp->GetActiveDocument ()->SetBondLength (length);
360 }
361 
GetPropertyPage()362 GtkWidget *gcpChainTool::GetPropertyPage ()
363 {
364 	gcugtk::UIBuilder *builder = new gcugtk::UIBuilder (UIDIR"/chain.ui", GETTEXT_PACKAGE);
365 	m_LengthBtn = GTK_SPIN_BUTTON (builder->GetWidget ("bond-length"));
366 	g_signal_connect (m_LengthBtn, "value-changed", G_CALLBACK (on_length_changed), this);
367 	m_AngleBtn = GTK_SPIN_BUTTON (builder->GetWidget ("bond-angle"));
368 	g_signal_connect (m_AngleBtn, "value-changed", G_CALLBACK (on_angle_changed), this);
369 	m_MergeBtn = GTK_TOGGLE_BUTTON (builder->GetWidget ("merge"));
370 	g_signal_connect (m_MergeBtn, "toggled", G_CALLBACK (on_merge_toggled), NULL);
371 	m_NumberBtn = GTK_SPIN_BUTTON (builder->GetWidget ("bonds-number"));
372 	gtk_widget_set_sensitive (GTK_WIDGET (m_NumberBtn), false);
373 	g_signal_connect (m_NumberBtn, "value-changed", G_CALLBACK (on_number_changed), this);
374 	m_AutoBtn = GTK_TOGGLE_BUTTON (builder->GetWidget ("auto-number"));
375 	gtk_toggle_button_set_active (m_AutoBtn, true);
376 	g_signal_connect (m_AutoBtn, "toggled", G_CALLBACK (on_number_toggled), this);
377 	GtkWidget *res = builder->GetRefdWidget ("chain");
378 	delete builder;
379 	return res;
380 }
381 
Activate()382 void gcpChainTool::Activate ()
383 {
384 	gcp::Document *pDoc = m_pApp->GetActiveDocument ();
385 	gtk_spin_button_set_value (m_LengthBtn, pDoc->GetBondLength ());
386 	gtk_spin_button_set_value (m_AngleBtn, pDoc->GetBondAngle ());
387 	gtk_toggle_button_set_active (m_MergeBtn, gcp::MergeAtoms);
388 }
389 
FindAtoms()390 void gcpChainTool::FindAtoms ()
391 {
392 	double x1 = m_Points->x, y1 = m_Points->y, a;
393 	unsigned nb;
394 	for (nb = 1; nb < m_CurPoints; nb++) {
395 		a = (m_dAngle +
396 			((m_Positive ^ (nb % 2))?
397 				90. - m_pView->GetDoc ()->GetBondAngle () / 2.:
398 				m_pView->GetDoc ()->GetBondAngle () / 2. - 90))
399 			* M_PI / 180.;
400 		x1 += m_BondLength * m_dZoomFactor * cos (a);
401 		y1 -= m_BondLength * m_dZoomFactor * sin (a);
402 		m_Atoms[nb] = NULL;
403 		if (gcp::MergeAtoms) {
404 			gccv::Canvas *canvas = m_pView->GetCanvas ();
405 			gccv::Item *item = canvas->GetItemAt (x1, y1);
406 			Object* pObject = NULL;
407 			if (item)
408 				pObject = dynamic_cast <Object *> (item->GetClient ());
409 			if (pObject && pObject != m_pObject) {
410 				if ((pObject->GetType () == BondType) || (pObject->GetType () == FragmentType)) {
411 					m_Atoms[nb] = (gcp::Atom*) pObject->GetAtomAt (x1 / m_dZoomFactor, y1 / m_dZoomFactor);
412 				} else if (pObject->GetType () == AtomType) {
413 					m_Atoms[nb] = (gcp::Atom*) pObject;
414 				}
415 			}
416 			if (m_Atoms[nb]) {
417 				m_Atoms[nb]->GetCoords(&x1, &y1, NULL);
418 				x1 *= m_dZoomFactor;
419 				y1 *= m_dZoomFactor;
420 			}
421 		}
422 		m_Points[nb].x = x1;
423 		m_Points[nb].y = y1;
424 	}
425 }
426 
CheckIfAllowed()427 bool gcpChainTool::CheckIfAllowed ()
428 {
429 	unsigned i, n;
430 	gcp::Document *pDoc = m_pView->GetDoc ();
431 	Object *group, *other;
432 	if (m_Atoms[0]) {
433 		group = m_Atoms[0]->GetMolecule ()->GetParent ();
434 		if (group == pDoc)
435 			group = NULL;
436 	} else
437 		group = NULL;
438 	for (i = 1; i < m_CurPoints; i++) {
439 		if (m_Atoms[i] == NULL)
440 			continue;
441 		if (group == NULL) {
442 			other = m_Atoms[i]->GetMolecule ()->GetParent ();
443 			if (other != pDoc)
444 				group = other;
445 		} else {
446 			other = m_Atoms[i]->GetMolecule ()->GetParent ();
447 			if (other && other != pDoc && other != group)
448 				return false;
449 		}
450 		n = (!m_Atoms[i]->GetBond(m_Atoms[i - 1]))? 1: 0;
451 		if ((i < m_CurPoints - 1) && !m_Atoms[i]->GetBond(m_Atoms[i + 1]))
452 			n++;
453 		if (n && !m_Atoms[i]->AcceptNewBonds (n))
454 			return false;
455 	}
456 	return true;
457 }
458 
OnKeyPress(GdkEvent * event)459 bool gcpChainTool::OnKeyPress (GdkEvent* event)
460 {
461 	if (event->type == GDK_KEY_PRESS) {
462 		unsigned n;
463 		switch (((GdkEventKey*) event)->keyval) {
464 		case GDK_KEY_KP_0:
465 		case GDK_KEY_0:
466 			gtk_toggle_button_set_active (m_AutoBtn, true);
467 			return true;
468 		case GDK_KEY_KP_1:
469 		case GDK_KEY_1:
470 			n = 10;
471 			break;
472 		case GDK_KEY_KP_2:
473 		case GDK_KEY_2:
474 			n = 2;
475 			break;
476 		case GDK_KEY_KP_3:
477 		case GDK_KEY_3:
478 			n = 3;
479 			break;
480 		case GDK_KEY_KP_4:
481 		case GDK_KEY_4:
482 			n = 4;
483 			break;
484 		case GDK_KEY_KP_5:
485 		case GDK_KEY_5:
486 			n = 5;
487 			break;
488 		case GDK_KEY_KP_6:
489 		case GDK_KEY_6:
490 			n = 6;
491 			break;
492 		case GDK_KEY_KP_7:
493 		case GDK_KEY_7:
494 			n = 7;
495 			break;
496 		case GDK_KEY_KP_8:
497 		case GDK_KEY_8:
498 			n = 8;
499 			break;
500 		case GDK_KEY_KP_9:
501 		case GDK_KEY_9:
502 			n = 9;
503 			break;
504 		default:
505 			return false;
506 		}
507 		gtk_toggle_button_set_active (m_AutoBtn, false);
508 		gtk_spin_button_set_value (m_NumberBtn, n);
509 		OnChangeState ();
510 	}
511 	return false;
512 }
513 
Draw()514 void gcpChainTool::Draw ()
515 {
516 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
517 	if (!m_Item)
518 		m_Item = new gccv::Group (m_pView->GetCanvas ());
519 	unsigned i;
520 	gccv::Item *item, *next;
521 	list <gccv::Item *>::iterator it;
522 	next = reinterpret_cast <gccv::Group *> (m_Item)->GetFirstChild (it);
523 	for (i = 1; i < m_CurPoints; i++) {
524 		if (next) {
525 			item = next;
526 			next = static_cast <gccv::Group *> (m_Item)->GetNextChild (it);
527 			static_cast <gccv::Line *> (item)->SetPosition (m_Points[i-1].x, m_Points[i-1].y, m_Points[i].x, m_Points[i].y);
528 		} else {
529 			item = new gccv::Line (static_cast <gccv::Group *> (m_Item), m_Points[i-1].x, m_Points[i-1].y, m_Points[i].x, m_Points[i].y);
530 			static_cast <gccv::LineItem *> (item)->SetLineColor (gcp::AddColor);
531 			static_cast <gccv::LineItem *> (item)->SetLineWidth (pTheme->GetBondWidth ());
532 		}
533 	}
534 	// now delete extra lines if any
535 	list <gccv::Item *> lines;
536 	while (next) {
537 		lines.push_front (next);
538 		next = static_cast <gccv::Group *> (m_Item)->GetNextChild (it);
539 	}
540 	while (!lines.empty ()) {
541 		delete lines.front ();
542 		lines.pop_front ();
543 	}
544 }
545