1 // -*- C++ -*-
2 
3 /*
4  * GChemPaint bonds plugin
5  * bondtool.cc
6  *
7  * Copyright (C) 2001-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 "bondtool.h"
27 #include <gcp/application.h>
28 #include <gcp/atom.h>
29 #include <gcp/bond.h>
30 #include <gcp/document.h>
31 #include <gcp/fragment.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/hash.h>
38 #include <gccv/line.h>
39 #include <gccv/squiggle.h>
40 #include <gccv/wedge.h>
41 #include <gcugtk/ui-builder.h>
42 #include <glib/gi18n-lib.h>
43 #include <cmath>
44 
45 using namespace gcu;
46 using namespace gccv;
47 using namespace std;
48 
gcpBondTool(gcp::Application * App,string ToolId,G_GNUC_UNUSED unsigned nPoints)49 gcpBondTool::gcpBondTool (gcp::Application *App, string ToolId, G_GNUC_UNUSED unsigned nPoints): gcp::Tool (App, ToolId)
50 {
51 	m_pOp = NULL;
52 	m_AutoDir = false;
53 }
54 
~gcpBondTool()55 gcpBondTool::~gcpBondTool ()
56 {
57 }
58 
OnClicked()59 bool gcpBondTool::OnClicked ()
60 {
61 	if (Element::GetMaxBonds (m_pApp->GetCurZ()) < 1)
62 		return false;
63 	int i;
64 	m_pAtom = NULL;
65 	m_bChanged = false;
66 	m_dAngle = 0.;
67 	gcp::Bond* pBond;
68 	gcp::Document* pDoc = m_pView->GetDoc ();
69 	if (m_pObject)
70 	{
71 		TypeId Id = m_pObject->GetType ();
72 		switch (Id)
73 		{
74 		case BondType:
75 			pBond = static_cast<gcp::Bond*> (m_pObject);
76 			if (pBond->IsLocked ())
77 				return false;
78 			m_pAtom = static_cast<gcp::Atom*> (pBond->GetAtom (0));
79 			m_pAtom->GetCoords (&m_x0, &m_y0, NULL);
80 			m_pAtom = static_cast<gcp::Atom*> (pBond->GetAtom (1));
81 			m_pAtom->GetCoords (&m_x1, &m_y1, NULL);
82 			m_x0 *= m_dZoomFactor;
83 			m_y0 *= m_dZoomFactor;
84 			m_x1 *= m_dZoomFactor;
85 			m_y1 *= m_dZoomFactor;
86 			m_bChanged = true;
87 			m_pOp = pDoc->GetNewOperation (gcp::GCP_MODIFY_OPERATION);
88 			m_pOp->AddObject (m_pObjectGroup, 0);
89 			UpdateBond ();
90 			return true;
91 		case FragmentType:
92 			m_pObject = static_cast <gcp::Fragment *> (m_pObject)->GetAtom ();
93 		case AtomType:
94 			if (!((gcp::Atom*) m_pObject)->AcceptNewBonds ())
95 				return false;
96 			((gcp::Atom*) m_pObject)->GetCoords (&m_x0, &m_y0, NULL);
97 			m_x0 *= m_dZoomFactor;
98 			m_y0 *=  m_dZoomFactor;
99 			/* search  preferred orientation for new bond */
100 			i = ((gcp::Atom*) m_pObject)->GetBondsNumber ();
101 			switch (i) {
102 				case 0:
103 					break;
104 				case 1: {
105 					map<Atom*, Bond*>::iterator i;
106 					gcp::Bond* bond = (gcp::Bond*) ((Atom*) m_pObject)->GetFirstBond (i);
107 					m_RefAngle = m_dAngle = bond->GetAngle2D ((gcp::Atom*) m_pObject);
108 					m_dAngle += (((m_nState & GDK_LOCK_MASK  && (!(m_nState & GDK_MOD5_MASK))) ||
109 								  ((!(m_nState & GDK_LOCK_MASK)) && m_nState & GDK_MOD5_MASK)))?
110 						pDoc->GetBondAngle (): -pDoc->GetBondAngle ();
111 					m_AutoDir = true;
112 					break;
113 				}
114 				case 2: {
115 					double a1, a2;
116 					map<Atom*, Bond*>::iterator i;
117 					gcp::Bond* bond = (gcp::Bond*) ((Atom*) m_pObject)->GetFirstBond (i);
118 					a1 = bond->GetAngle2D ((gcp::Atom*) m_pObject);
119 					bond = (gcp::Bond*) ((Atom*) m_pObject)->GetNextBond (i);
120 					a2 = bond->GetAngle2D ((gcp::Atom*) m_pObject);
121 					m_dAngle = (a1 + a2) / 2.;
122 					a2 = fabs (a2 - m_dAngle);
123 					if (a2 < 90.)
124 						m_dAngle += 180.;
125 					if (m_dAngle > 360.)
126 						m_dAngle -= 360.;
127 					break;
128 				}
129 				default:
130 					break;
131 			}
132 			break;
133 		default:
134 			return false;
135 		}
136 	}
137 	double a = m_dAngle * M_PI / 180.;
138 	m_x1 =  m_x0 + pDoc->GetBondLength () * m_dZoomFactor * cos (a);
139 	m_y1 =  m_y0 - pDoc->GetBondLength () * m_dZoomFactor * sin (a);
140 	// TODO: reimplement
141 	Item *pItem = m_pView->GetCanvas ()->GetItemAt (m_x1, m_y1);
142 	Object* pObject = NULL;
143 	if (pItem)
144 		pObject = dynamic_cast <Object *> (pItem->GetClient ());
145 	m_pAtom = NULL;
146 	if (pObject && pObject != m_pObject) {
147 		if ((pObject->GetType () == BondType) || (pObject->GetType () == FragmentType))
148 		{
149 			m_pAtom = (gcp::Atom*) pObject->GetAtomAt (m_x1 / m_dZoomFactor, m_y1 / m_dZoomFactor);
150 		} else if (pObject->GetType() == AtomType) {
151 			m_pAtom = (gcp::Atom*)pObject;
152 		}
153 	}
154 	if (m_pAtom) {
155 		if (m_pObject) {
156 			Object *group = m_pObject->GetMolecule ()->GetParent ();
157 			if (group != pDoc) {
158 				Object *other = m_pAtom->GetMolecule ()->GetParent ();
159 				if (other != pDoc && group != other)
160 					return true;
161 			}
162 		}
163 		m_pAtom->GetCoords(&m_x1, &m_y1, NULL);
164 		m_x1 *= m_dZoomFactor;
165 		m_y1 *= m_dZoomFactor;
166 		m_x = m_x1 - m_x0;
167 		m_y = m_y1 - m_y0;
168 		m_dAngle = atan(-m_y/m_x) * 90 / 1.570796326794897;
169 		if (m_x < 0) m_dAngle += 180;
170 	}
171 	char tmp[32];
172 	snprintf (tmp, sizeof (tmp) - 1, _("Orientation: %g"), m_dAngle);
173 	m_pApp->SetStatusText (tmp);
174 	Draw ();
175 	return true;
176 }
177 
OnDrag()178 void gcpBondTool::OnDrag ()
179 {
180 	gcp::Document* pDoc = m_pView->GetDoc ();
181 	gcp::Theme *Theme = pDoc->GetTheme ();
182 	if ((m_pObject) && (m_pObject->GetType () == BondType)) {
183 		if (((gcp::Bond*) m_pObject)->GetDist (m_x / m_dZoomFactor, m_y / m_dZoomFactor) < (Theme->GetPadding () + Theme->GetBondWidth () / 2) * m_dZoomFactor) {
184 			if (!m_bChanged) {
185 				m_Item->SetVisible (true);
186 				m_bChanged = true;
187 			}
188 		} else if (m_bChanged) {
189 			m_Item->SetVisible (false);
190 			m_bChanged = false;
191 		}
192 	} else {
193 		Item *pItem = m_pView->GetCanvas ()->GetItemAt (m_x, m_y);
194 		Object* pObject = NULL;
195 		if (pItem) {
196 			pObject = dynamic_cast <Object *> (pItem->GetClient ());
197 			if (pObject && (pObject == m_pObject || ((pObject->GetType () == FragmentType) && dynamic_cast<gcp::Fragment*> (pObject)->GetAtom () == m_pObject))) {
198 				if (!m_AutoDir)
199 					return;
200 			} else
201 				m_AutoDir = false;
202 		} else
203 			m_AutoDir = false;
204 		double dAngle = 0.;
205 		if (m_AutoDir) {
206 			dAngle = m_dAngle = m_RefAngle +
207 						((((m_nState & GDK_LOCK_MASK  && (!(m_nState & GDK_MOD5_MASK))) ||
208 						((!(m_nState & GDK_LOCK_MASK)) && m_nState & GDK_MOD5_MASK)))?
209 						pDoc->GetBondAngle (): -pDoc->GetBondAngle ());
210 			m_x = m_x1 = m_x0 + pDoc->GetBondLength () * m_dZoomFactor * cos (m_dAngle / 180 * M_PI);
211 			m_y = m_y1 = m_y0 - pDoc->GetBondLength () * m_dZoomFactor * sin (m_dAngle / 180 * M_PI);
212 			pItem = m_pView->GetCanvas ()->GetItemAt (m_x, m_y);
213 			pObject = NULL;
214 			if (pItem)
215 				pObject = dynamic_cast <Object *> (pItem->GetClient ());
216 		}
217 		m_pAtom = NULL;
218 		if (gcp::MergeAtoms && pObject) {
219 			if (pObject->GetType () == BondType)
220 				m_pAtom = (gcp::Atom*) pObject->GetAtomAt (m_x / m_dZoomFactor, m_y / m_dZoomFactor);
221 			else if (pObject->GetType () == FragmentType)
222 				m_pAtom = (gcp::Atom*)pObject->GetAtomAt (m_x1 / m_dZoomFactor, m_y1 / m_dZoomFactor);
223 			else if (pObject->GetType () == AtomType)
224 				m_pAtom = (gcp::Atom*) pObject;
225 		}
226 		if (m_pAtom) {
227 			if (m_pObject) {
228 				Object *group = m_pObject->GetMolecule ()->GetParent ();
229 				if (group != pDoc) {
230 					Object *other = m_pAtom->GetMolecule ()->GetParent ();
231 					if (other != pDoc && group != other)
232 						return;
233 				}
234 			}
235 			if ((Object*) m_pAtom == m_pObject)
236 				return;
237 			if (!m_pAtom->AcceptNewBonds ())
238 				return;
239 			m_pAtom->GetCoords (&m_x1, &m_y1, NULL);
240 			m_x1 *= m_dZoomFactor;
241 			m_y1 *= m_dZoomFactor;
242 			m_x = m_x1 - m_x0;
243 			m_y = m_y1 - m_y0;
244 			dAngle = atan (-m_y / m_x) * 180. / M_PI;
245 			if (isnan (dAngle))
246 				dAngle = m_dAngle;
247 			else if (m_x < 0.)
248 				dAngle += 180.;
249 		} else if (!m_AutoDir) {
250 			m_x -= m_x0;
251 			m_y -= m_y0;
252 			if (m_x == 0) {
253 				if (m_y == 0)
254 					return;
255 				dAngle = (m_y < 0) ? 90 : 270;
256 			} else {
257 				dAngle = atan (-m_y/m_x) * 180. / M_PI;
258 				if (!(m_nState & GDK_CONTROL_MASK))
259 					dAngle = rint (dAngle / 5) * 5;
260 				if (isnan (dAngle))
261 					dAngle = m_dAngle;
262 				else if (m_x < 0.)
263 					dAngle += 180.;
264 			}
265 			m_dAngle = dAngle;
266 			if (m_nState & GDK_SHIFT_MASK) {
267 				double x1 = sqrt (square (m_x) + square (m_y));
268 				m_x1 = m_x0 + x1 * cos (m_dAngle / 180 * M_PI);
269 				m_y1 = m_y0 - x1 * sin (m_dAngle / 180 * M_PI);
270 			} else {
271 				m_x1 = m_x0 + pDoc->GetBondLength () * m_dZoomFactor * cos (m_dAngle / 180 * M_PI);
272 				m_y1 = m_y0 - pDoc->GetBondLength () * m_dZoomFactor * sin (m_dAngle / 180 * M_PI);
273 			}
274 		}
275 		char tmp[32];
276 		if (dAngle < 0)
277 			dAngle += 360.;
278 		snprintf (tmp, sizeof (tmp) - 1, _("Orientation: %g"), dAngle);
279 		m_pApp->SetStatusText (tmp);
280 		Draw ();
281 	}
282 }
283 
OnRelease()284 void gcpBondTool::OnRelease ()
285 {
286 	gcp::Document* pDoc = m_pView->GetDoc ();
287 	if (m_Item) {
288 		delete m_Item;
289 		m_Item = NULL;
290 	} else {
291 		if (m_pOp)
292 			pDoc->AbortOperation ();
293 		m_pOp = NULL;
294 		return;
295 	}
296 	if ((m_pObject) && (m_pObject->GetType () == BondType)) {
297 		FinalizeBond ();
298 		gcp::Atom* pAtom = (gcp::Atom*) ((gcp::Bond*) m_pObject)->GetAtom (0);
299 		pAtom->Update ();
300 		m_pView->Update (pAtom);
301 		pAtom = (gcp::Atom*) ((gcp::Bond*) m_pObject)->GetAtom (1);
302 		pAtom->Update ();
303 		m_pView->Update (pAtom);
304 		m_pOp->AddObject (m_pObjectGroup, 1);
305 		pDoc->FinishOperation ();
306 		m_pOp = NULL;
307 		m_pObject->EmitSignal (gcp::OnChangedSignal);
308 		return;
309 	} else {
310 		if (m_pOp)
311 			pDoc->AbortOperation();
312 		m_pOp = NULL;
313 	}
314 	m_pApp->ClearStatus ();
315 	Item *pItem = m_pView->GetCanvas ()->GetItemAt (m_x1, m_y1);
316 	Object* pObject = NULL;
317 	if (pItem)
318 		pObject = dynamic_cast <Object *> (pItem->GetClient ());
319 	m_pAtom = NULL;
320 	if (gcp::MergeAtoms && pObject) {
321 		if (pObject->GetType () == BondType)
322 			m_pAtom = (gcp::Atom*) pObject->GetAtomAt (m_x1 / m_dZoomFactor, m_y1 / m_dZoomFactor);
323 		else if (pObject->GetType() == FragmentType)
324 			m_pAtom = (gcp::Atom*) pObject->GetAtomAt (m_x1 / m_dZoomFactor, m_y1 / m_dZoomFactor);
325 		else if (pObject->GetType() == AtomType)
326 			m_pAtom = (gcp::Atom*) pObject;
327 	}
328 	gcp::Atom* pAtom;
329 	gcp::Bond* pBond;
330 	if (!m_pObject) {
331 		//Add an atom at (x0, y0)
332 		pAtom = new gcp::Atom (m_pApp->GetCurZ(), m_x0 / m_dZoomFactor, m_y0 / m_dZoomFactor, 0);
333 		pDoc->AddAtom (pAtom);
334 		m_pObject = pAtom;
335 	} else {
336 		pObject = m_pObject->GetGroup ();
337 		if (pObject)
338 			ModifiedObjects.insert (pObject->GetId ());
339 	}
340 	if (m_pObject->GetType () == AtomType) {
341 		if (m_pAtom) {
342 			if (m_pObject == m_pAtom) {
343 				ModifiedObjects.clear ();
344 				return;
345 			}
346 			pObject = m_pAtom->GetGroup ();
347 			if (!pObject)
348 				throw runtime_error (_("Invalid document tree, please file a bug report"));
349 			ModifiedObjects.insert (pObject->GetId ());
350 			pAtom = m_pAtom;
351 		} else {
352 			pAtom = new gcp::Atom (m_pApp->GetCurZ (), m_x1 / m_dZoomFactor, m_y1 / m_dZoomFactor, 0);
353 			pDoc->AddAtom (pAtom);
354 		}
355 		pBond = (gcp::Bond*) pAtom->GetBond ((gcp::Atom*) m_pObject);
356 		if (pBond) {
357 			m_pOp = pDoc-> GetNewOperation (gcp::GCP_MODIFY_OPERATION);
358 			m_pOp->AddObject (pBond->GetGroup (), 0);
359 			if (pBond->GetType () == gcp::NormalBondType)
360 				pBond->IncOrder ();
361 			m_pObject = pBond;
362 			m_bChanged = true;
363 			FinalizeBond ();
364 			gcp::Atom* pAtom = (gcp::Atom*) ((gcp::Bond*) m_pObject)->GetAtom (0);
365 			pAtom->Update ();
366 			m_pView->Update (pAtom);
367 			pAtom = (gcp::Atom*) ((gcp::Bond*) m_pObject)->GetAtom (1);
368 			pAtom->Update ();
369 			m_pView->Update (pAtom);
370 			m_pView->Update (pBond);
371 			m_pOp->AddObject (pBond->GetGroup (), 1);
372 			pDoc->FinishOperation ();
373 			m_pOp = NULL;
374 		} else {
375 			// Push modified objects in Operation
376 			if (ModifiedObjects.size ()) {
377 				m_pOp = pDoc-> GetNewOperation (gcp::GCP_MODIFY_OPERATION);
378 				set<string>::iterator it, end = ModifiedObjects.end ();
379 				for (it = ModifiedObjects.begin (); it != end; it++)
380 					m_pOp->AddObject (pDoc->GetDescendant ((*it).c_str ()), 0);
381 			}
382 			pBond = new gcp::Bond ((gcp::Atom*) m_pObject, pAtom, 1);
383 			SetType (pBond);
384 			pDoc->AddBond (pBond);
385 			if (m_pOp) {
386 				set<string>::iterator it, end = ModifiedObjects.end ();
387 				for (it = ModifiedObjects.begin (); it != end; it++) {
388 					pObject = pDoc->GetDescendant ((*it).c_str ());
389 					if (pObject)
390 						m_pOp->AddObject (pObject, 1);
391 				}
392 			} else {
393 				m_pOp = pDoc-> GetNewOperation (gcp::GCP_ADD_OPERATION);
394 				m_pOp->AddObject (pBond->GetMolecule ());
395 			}
396 			pDoc->FinishOperation ();
397 		}
398 	}
399 	ModifiedObjects.clear ();
400 }
401 
Draw()402 void gcpBondTool::Draw ()
403 {
404 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
405 	if (m_Item) {
406 		static_cast <Line *> (m_Item)->SetPosition (m_x0, m_y0, m_x1, m_y1);
407 	} else {
408 		m_Item = new gccv::Line (m_pView->GetCanvas (), m_x0, m_y0, m_x1, m_y1);
409 		static_cast <LineItem *> (m_Item)->SetLineColor (gcp::AddColor);
410 		static_cast <LineItem *> (m_Item)->SetLineWidth (pTheme->GetBondWidth ());
411 	}
412 }
413 
UpdateBond()414 void gcpBondTool::UpdateBond()
415 {
416 	double x1, y1, x2, y2;
417 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
418 	gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
419 	BondOrder = bond->GetOrder ();
420 	if (bond->GetType () == gcp::NormalBondType)
421 		bond->IncOrder ();
422 	if (m_Item)
423 		delete m_Item;
424 	if (bond->GetOrder () == 1) {
425 		bond->GetLine2DCoords (1, &x1, &y1, &x2, &y2);
426 		m_Item = new gccv::Line (m_pView->GetCanvas (),
427 								 x1 * m_dZoomFactor, y1 * m_dZoomFactor,
428 								 x2 * m_dZoomFactor, y2 * m_dZoomFactor);
429 		static_cast <LineItem *> (m_Item)->SetLineColor (gcp::AddColor);
430 		static_cast <LineItem *> (m_Item)->SetLineWidth (pTheme->GetBondWidth ());
431 	} else {
432 		int i = 1;
433 		m_Item = new gccv::Group (m_pView->GetCanvas ());
434 		while (((gcp::Bond*) m_pObject)->GetLine2DCoords (i++, &x1, &y1, &x2, &y2)) {
435 			gccv::LineItem *item = new gccv::Line (static_cast <gccv::Group *> (m_Item),
436 												   x1 * m_dZoomFactor, y1 * m_dZoomFactor,
437 												   x2 * m_dZoomFactor, y2 * m_dZoomFactor);
438 			item->SetLineColor (gcp::AddColor);
439 			item->SetLineWidth (pTheme->GetBondWidth ());
440 		}
441 	}
442 }
443 
FinalizeBond()444 void gcpBondTool::FinalizeBond ()
445 {
446 	if (m_bChanged) {
447 		gcp::Bond* pBond = (gcp::Bond*) m_pObject;
448 		if (pBond->GetType () == gcp::NormalBondType)
449 			m_pView->Update (m_pObject);
450 		else {
451 			pBond->SetType (gcp::NormalBondType);
452 			m_pView->Update (m_pObject);
453 		}
454 	}
455 	else ((gcp::Bond*) m_pObject)->SetOrder (BondOrder);
456 	m_pView->Update(((gcp::Bond*) m_pObject)->GetAtom (0));
457 	m_pView->Update(((gcp::Bond*) m_pObject)->GetAtom (1));
458 }
459 
SetType(gcp::Bond * pBond)460 void gcpBondTool::SetType (gcp::Bond* pBond)
461 {
462 	pBond->SetType (gcp::NormalBondType);
463 }
464 
on_length_changed(GtkSpinButton * btn,gcpBondTool * tool)465 static void on_length_changed (GtkSpinButton *btn, gcpBondTool *tool)
466 {
467 	tool->SetLength (gtk_spin_button_get_value (btn));
468 }
469 
on_angle_changed(GtkSpinButton * btn,gcpBondTool * tool)470 static void on_angle_changed (GtkSpinButton *btn, gcpBondTool *tool)
471 {
472 	tool->SetAngle (gtk_spin_button_get_value (btn));
473 }
474 
on_merge_toggled(GtkToggleButton * btn)475 static void on_merge_toggled (GtkToggleButton *btn)
476 {
477 	gcp::MergeAtoms = gtk_toggle_button_get_active (btn);
478 }
479 
SetAngle(double angle)480 void gcpBondTool::SetAngle (double angle)
481 {
482 	m_pApp->GetActiveDocument ()->SetBondAngle (angle);
483 }
484 
SetLength(double length)485 void gcpBondTool::SetLength (double length)
486 {
487 	m_pApp->GetActiveDocument ()->SetBondLength (length);
488 }
489 
GetPropertyPage()490 GtkWidget *gcpBondTool::GetPropertyPage ()
491 {
492 	gcugtk::UIBuilder *builder = new gcugtk::UIBuilder (UIDIR"/bond.ui", GETTEXT_PACKAGE);
493 	m_LengthBtn = GTK_SPIN_BUTTON (builder->GetWidget ("bond-length"));
494 	g_signal_connect (m_LengthBtn, "value-changed", G_CALLBACK (on_length_changed), this);
495 	m_AngleBtn = GTK_SPIN_BUTTON (builder->GetWidget ("bond-angle"));
496 	g_signal_connect (m_AngleBtn, "value-changed", G_CALLBACK (on_angle_changed), this);
497 	m_MergeBtn = GTK_TOGGLE_BUTTON (builder->GetWidget ("merge"));
498 	g_signal_connect (m_MergeBtn, "toggled", G_CALLBACK (on_merge_toggled), NULL);
499 	GtkWidget *res = builder->GetRefdWidget ("bond");
500 	delete builder;
501 	return res;
502 }
503 
Activate()504 void gcpBondTool::Activate ()
505 {
506 	gcp::Document *pDoc = m_pApp->GetActiveDocument ();
507 	gtk_spin_button_set_value (m_LengthBtn, pDoc->GetBondLength ());
508 	gtk_spin_button_set_value (m_AngleBtn, pDoc->GetBondAngle ());
509 	gtk_toggle_button_set_active (m_MergeBtn, gcp::MergeAtoms);
510 }
511 
gcpUpBondTool(gcp::Application * App)512 gcpUpBondTool::gcpUpBondTool (gcp::Application *App): gcpBondTool (App, "UpBond", 3)
513 {
514 }
515 
~gcpUpBondTool()516 gcpUpBondTool::~gcpUpBondTool ()
517 {
518 }
519 
Draw()520 void gcpUpBondTool::Draw ()
521 {
522 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
523 	if (m_Item) {
524 		static_cast <Wedge *> (m_Item)->SetPosition (m_x0, m_y0, m_x1, m_y1);
525 	} else {
526 		m_Item = new Wedge (m_pView->GetCanvas (), m_x0, m_y0, m_x1, m_y1, pTheme->GetStereoBondWidth ());
527 		static_cast <Wedge *> (m_Item)->SetFillColor (gcp::AddColor);
528 	}
529 }
530 
UpdateBond()531 void gcpUpBondTool::UpdateBond ()
532 {
533 	if (((gcp::Bond*) m_pObject)->GetType () == gcp::UpBondType) {
534 		m_x = m_x0;
535 		m_x0 = m_x1;
536 		m_x1 = m_x;
537 		m_y = m_y0;
538 		m_y0 = m_y1;
539 		m_y1 = m_y;
540 	}
541 	Draw ();
542 }
543 
FinalizeBond()544 void gcpUpBondTool::FinalizeBond()
545 {
546 	if (m_bChanged) {
547 		gcp::Bond* pBond = (gcp::Bond*) m_pObject;
548 		if (pBond->GetType () == gcp::UpBondType) {
549 			pBond->Revert ();
550 			m_pView->Update (m_pObject);
551 		} else {
552 			pBond->SetType (gcp::UpBondType);
553 			m_pView->Remove (m_pObject);
554 			m_pView->AddObject (m_pObject);
555 		}
556 	}
557 }
558 
SetType(gcp::Bond * pBond)559 void gcpUpBondTool::SetType (gcp::Bond* pBond)//FIXME: Is it really useful?
560 {
561 	pBond->SetType (gcp::UpBondType);
562 }
563 
on_config_changed(GOConfNode * node,gchar const *,gcpDownBondTool * tool)564 static void on_config_changed (GOConfNode *node, gchar const *,  gcpDownBondTool *tool)
565 {
566 	tool->UpdateItem (go_conf_get_bool (node, "invert-wedge-hashes"));
567 }
568 
gcpDownBondTool(gcp::Application * App,gccv::Wedge * item)569 gcpDownBondTool::gcpDownBondTool (gcp::Application *App, gccv::Wedge *item):
570 	gcpBondTool (App, "DownBond", 4),
571 	m_Wedge (item)
572 {
573 	m_ConfNode = go_conf_get_node (App->GetConfDir (), GCP_CONF_DIR_SETTINGS);
574 	m_NotificationId = go_conf_add_monitor (m_ConfNode, NULL, (GOConfMonitorFunc) on_config_changed, GetApplication ());
575 }
576 
~gcpDownBondTool()577 gcpDownBondTool::~gcpDownBondTool ()
578 {
579 	go_conf_remove_monitor (m_NotificationId);
580 	go_conf_free_node (m_ConfNode);
581 }
582 
UpdateItem(bool inverted)583 void gcpDownBondTool::UpdateItem (bool inverted)
584 {
585 	if (inverted)
586 		m_Wedge->SetPosition (2., 22., 19., 5.);
587 	else
588 		m_Wedge->SetPosition (19., 5., 2., 22.);
589 }
590 
Draw()591 void gcpDownBondTool::Draw()
592 {
593 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
594 	if (m_Item) {
595 		if (gcp::InvertWedgeHashes)
596 			static_cast <Hash *> (m_Item)->SetPosition (m_x0, m_y0, m_x1, m_y1);
597 		else
598 			static_cast <Hash *> (m_Item)->SetPosition (m_x1, m_y1, m_x0, m_y0);
599 	} else {
600 		gccv::Hash *hash = gcp::InvertWedgeHashes?
601 			new Hash (m_pView->GetCanvas (), m_x0, m_y0, m_x1, m_y1, pTheme->GetStereoBondWidth ()):
602 			new Hash (m_pView->GetCanvas (), m_x1, m_y1, m_x0, m_y0, pTheme->GetStereoBondWidth ());
603 		hash->SetFillColor (gcp::AddColor);
604 		hash->SetLineWidth (pTheme->GetHashWidth ());
605 		hash->SetLineDist (pTheme->GetHashDist ());
606 		m_Item = hash;
607 	}
608 }
609 
UpdateBond()610 void gcpDownBondTool::UpdateBond ()
611 {
612 	if (((gcp::Bond*) m_pObject)->GetType() == gcp::DownBondType) {
613 		m_x = m_x0;
614 		m_x0 = m_x1;
615 		m_x1 = m_x;
616 		m_y = m_y0;
617 		m_y0 = m_y1;
618 		m_y1 = m_y;
619 	}
620 	Draw ();
621 }
622 
FinalizeBond()623 void gcpDownBondTool::FinalizeBond ()
624 {
625 	if (m_bChanged) {
626 		gcp::Bond* pBond = (gcp::Bond*)m_pObject;
627 		if (pBond->GetType () == gcp::DownBondType) {
628 			pBond->Revert ();
629 			m_pView->Update (m_pObject);
630 		} else {
631 			pBond->SetType (gcp::DownBondType);
632 			m_pView->Remove (m_pObject);
633 			m_pView->AddObject (m_pObject);
634 		}
635 	}
636 }
637 
SetType(gcp::Bond * pBond)638 void gcpDownBondTool::SetType (gcp::Bond* pBond)
639 {
640 	pBond->SetType (gcp::DownBondType);
641 }
642 
gcpForeBondTool(gcp::Application * App)643 gcpForeBondTool::gcpForeBondTool (gcp::Application *App): gcpBondTool (App, "ForeBond", 4)
644 {
645 }
646 
~gcpForeBondTool()647 gcpForeBondTool::~gcpForeBondTool ()
648 {
649 }
650 
Draw()651 void gcpForeBondTool::Draw ()
652 {
653 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
654 	if (m_Item) {
655 		static_cast <Line *> (m_Item)->SetPosition (m_x0, m_y0, m_x1, m_y1);
656 	} else {
657 		m_Item = new gccv::Line (m_pView->GetCanvas (), m_x0, m_y0, m_x1, m_y1);
658 		static_cast <LineItem *> (m_Item)->SetLineColor (gcp::AddColor);
659 		static_cast <LineItem *> (m_Item)->SetLineWidth (pTheme->GetStereoBondWidth ());
660 	}
661 }
662 
UpdateBond()663 void gcpForeBondTool::UpdateBond ()
664 {
665 	Draw ();
666 }
667 
FinalizeBond()668 void gcpForeBondTool::FinalizeBond ()
669 {
670 	if (m_bChanged) {
671 		gcp::Bond* pBond = (gcp::Bond*) m_pObject;
672 		if (pBond->GetType () != gcp::ForeBondType) {
673 			pBond->SetType (gcp::ForeBondType);
674 			m_pView->Remove (m_pObject);
675 			m_pView->AddObject (m_pObject);
676 		}
677 	}
678 }
679 
SetType(gcp::Bond * pBond)680 void gcpForeBondTool::SetType (gcp::Bond* pBond)
681 {
682 	pBond->SetType (gcp::ForeBondType);
683 }
684 
gcpSquiggleBondTool(gcp::Application * App)685 gcpSquiggleBondTool::gcpSquiggleBondTool (gcp::Application *App): gcpBondTool (App, "SquiggleBond", 4)
686 {
687 }
688 
~gcpSquiggleBondTool()689 gcpSquiggleBondTool::~gcpSquiggleBondTool ()
690 {
691 }
692 
Draw()693 void gcpSquiggleBondTool::Draw ()
694 {
695 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
696 	if (m_Item) {
697 		static_cast <Squiggle *> (m_Item)->SetPosition (m_x0, m_y0, m_x1, m_y1);
698 	} else {
699 		Squiggle *squiggle = new Squiggle (m_pView->GetCanvas (), m_x0, m_y0, m_x1, m_y1);
700 		squiggle->SetLineColor (gcp::AddColor);
701 		squiggle->SetLineWidth (pTheme->GetBondWidth ());
702 		squiggle->SetWidth (pTheme->GetStereoBondWidth () - pTheme->GetBondWidth () / 2.);
703 		squiggle->SetStep (pTheme->GetStereoBondWidth () / 2.);
704 		m_Item = squiggle;
705 	}
706 }
707 
UpdateBond()708 void gcpSquiggleBondTool::UpdateBond ()
709 {
710 	Draw ();
711 }
712 
FinalizeBond()713 void gcpSquiggleBondTool::FinalizeBond ()
714 {
715 	if (m_bChanged) {
716 		gcp::Bond* pBond = (gcp::Bond*) m_pObject;
717 		if (pBond->GetType () == gcp::UndeterminedBondType) {
718 			pBond->Revert ();
719 			m_pView->Update (m_pObject);
720 		} else {
721 			pBond->SetType (gcp::UndeterminedBondType);
722 			m_pView->Remove (m_pObject);
723 			m_pView->AddObject (m_pObject);
724 		}
725 	}
726 }
727 
SetType(gcp::Bond * pBond)728 void gcpSquiggleBondTool::SetType (gcp::Bond* pBond)
729 {
730 	pBond->SetType (gcp::UndeterminedBondType);
731 }
732