1 // -*- C++ -*-
2 
3 /*
4  * GChemPaint arrows plugin
5  * curvedarrowtool.cc
6  *
7  * Copyright (C) 2004-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 "curvedarrowtool.h"
27 #include <gccv/bezier-arrow.h>
28 #include <gccv/canvas.h>
29 #include <gcp/application.h>
30 #include <gcp/atom.h>
31 #include <gcp/bond.h>
32 #include <gcp/document.h>
33 #include <gcp/electron.h>
34 #include <gcp/mechanism-arrow.h>
35 #include <gcp/mechanism-step.h>
36 #include <gcp/mesomer.h>
37 #include <gcp/molecule.h>
38 #include <gcp/reaction-step.h>
39 #include <gcp/settings.h>
40 #include <gcp/theme.h>
41 #include <gcp/view.h>
42 #include <gcp/widgetdata.h>
43 #include <gcugtk/ui-builder.h>
44 #include <gtk/gtk.h>
45 
46 using namespace std;
47 
gcpCurvedArrowTool(gcp::Application * App,string Id)48 gcpCurvedArrowTool::gcpCurvedArrowTool (gcp::Application *App, string Id): gcp::Tool (App, Id)
49 {
50 	m_Full = Id == "CurvedArrow";
51 	if (m_Full) {
52 		GOConfNode *node = go_conf_get_node (gcu::Application::GetConfDir (), "paint/plugins/arrows");
53 		m_EndAtBondCenter = go_conf_get_bool (node, "end-at-new-bond-center");
54 		go_conf_free_node (node);
55 	} else
56 		m_EndAtBondCenter = true;
57 }
58 
~gcpCurvedArrowTool()59 gcpCurvedArrowTool::~gcpCurvedArrowTool ()
60 {
61 }
62 
OnClicked()63 bool gcpCurvedArrowTool::OnClicked ()
64 {
65 	gcp::Document *pDoc = m_pView->GetDoc ();
66 	gcp::Theme *pTheme = pDoc->GetTheme ();
67 	gccv::BezierArrow *arrow = NULL; // make g++ happy
68 	m_SourceAux = NULL;
69 	m_Target = NULL;
70 	m_pData->UnselectAll ();
71 	if (m_pObject)
72 		switch (m_pObject->GetType ()) {
73 		case gcu::FragmentType:
74 			m_pObject = static_cast <gcp::Fragment *> (m_pObject)->GetAtom ();
75 		case gcu::AtomType: {
76 			gcp::Atom *atom = static_cast <gcp::Atom *> (m_pObject);
77 			if (!AllowAsSource (atom))
78 			    return false;
79 			// find if there is an explicit electron pair or single electron and use it as source
80 			std::map <std::string, gcu::Object *>::iterator it;
81 			gcu::Object *obj;
82 			gcp::Electron *elec = NULL, *cur;
83 			double x, y, angle, dist, a0;
84 			atom->GetCoords (&x, &y);
85 			x *= pTheme->GetZoomFactor ();
86 			y *= pTheme->GetZoomFactor ();
87 			a0 = (x == m_x0 && y == m_y0)? go_nan: atan2 (y - m_y0, m_x0 - x);
88 			for (obj = atom->GetFirstChild (it); obj; obj = atom->GetNextChild (it)) {
89 				cur = dynamic_cast <gcp::Electron *> (obj);
90 				if (!cur || (m_Full && !cur->IsPair ()))
91 					continue;
92 				cur->GetPosition (&angle, &dist);
93 				if (elec) {
94 				} else {
95 					elec = cur;
96 					if (isnan (a0))
97 						break;
98 				}
99 			}
100 			if (elec) {
101 				elec->GetPosition (&a0, &dist);
102 				a0 *= M_PI / 180.;
103 				m_pObject = elec;
104 			}
105 			// find the most probable bond or the nearest atom
106 			if (atom->GetBondsNumber () > 0) {
107 				m_Target = atom->GetBondAtAngle (a0);
108 				m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
109 				if (!AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
110 					m_Target = NULL;
111 				if (m_Target == NULL)
112 					break;
113 				if (m_pObject == atom)
114 					AtomToAdjBond ();
115 				else
116 					ElectronToAdjBond ();
117 			} else {
118 				// try to find a possible atom target
119 				// TODO: implement and return true
120 				m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
121 				break;
122 			}
123 			break;
124 		}
125 		case gcu::BondType: {
126 			gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
127 			if (!AllowAsSource (bond))
128 			    return false;
129 			m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
130 			BondToAdjAtom ();
131 			break;
132 		}
133 		default:
134 			if (m_pObject->GetType () == gcp::ElectronType) {
135 				gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
136 				if (!AllowAsSource (elec))
137 					return false;
138 				gcu::Object *obj = m_pObject->GetParent ();
139 				gcp::Atom *atom = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
140 				double x, a0;
141 				elec->GetPosition (&a0, &x);
142 				a0 *= M_PI / 180.;
143 				// find the most probable bond or the nearest atom
144 				if (atom->GetBondsNumber () > 0) {
145 					m_Target = atom->GetBondAtAngle (a0);
146 					m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
147 					if (!AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
148 						m_Target = NULL;
149 					if (m_Target == NULL)
150 						break;
151 					ElectronToAdjBond ();
152 				} else {
153 					// try to find a possible atom target
154 					// TODO: implement and return true
155 					m_Item = arrow = new gccv::BezierArrow (m_pView->GetCanvas ());
156 				}
157 				break;
158 			} else if (m_pObject->GetType () == gcp::MechanismArrowType) {
159 				// select the arrow and show the control points
160 				m_Item = arrow = static_cast <gccv::BezierArrow *> (dynamic_cast <gccv::ItemClient *> (m_pObject)->GetItem ());
161 				m_x0 = -1; // to make clear we have not hit a control point yet
162 				arrow->GetControlPoints (m_CPx0, m_CPy0, m_CPx1, m_CPy1, m_CPx2, m_CPy2, m_CPx3, m_CPy3);
163 				// store control point half width in m_y0
164 				m_y0 = arrow->GetLineWidth () * 2.5;
165 			}
166 			break;
167 		}
168 	if (arrow) {
169 		arrow->SetShowControls (true);
170 		arrow->SetLineWidth (pTheme->GetArrowWidth ());
171 		arrow->SetLineColor (gcp::AddColor);
172 	}
173 	return true;
174 }
175 
OnDrag()176 void gcpCurvedArrowTool::OnDrag ()
177 {
178 	if (!m_Item || !m_pObject)
179 		return;	// this should not occur
180 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
181 	gccv::BezierArrow *arrow = static_cast <gccv::BezierArrow *> (m_Item);
182 	if (m_pObject->GetType () == gcp::MechanismArrowType) {
183 			if (m_x0 < 0.) {
184 				if (m_x >= m_CPx1 - m_y0 && m_x <= m_CPx1 + m_y0 && m_y >= m_CPy1 - m_y0 && m_y <= m_CPy1 + m_y0) {
185 					// we are inside the second control point
186 					m_x0 = m_x;
187 					m_y0 = m_y;
188 					// store original values
189 					m_x1 = m_CPx1;
190 					m_y1 = m_CPy1;
191 				} else if (m_x >=m_CPx2 - m_y0 && m_x <= m_CPx2 + m_y0 && m_y >= m_CPy2 - m_y0 && m_y <= m_CPy2 + m_y0) {
192 					// we are inside the third control point
193 					m_x0 = m_x;
194 					m_y0 = m_y;
195 					m_Target = m_pObject; // to differentiate between the two control points
196 					// store original values
197 					m_x1 = m_CPx2;
198 					m_y1 = m_CPy2;
199 				}
200 			} else {
201 			// TODO: implement moving the control points
202 				if (m_Target) {
203 					m_CPx2 = m_x1 + m_x - m_x0;
204 					m_CPy2 = m_y1 + m_y - m_y0;
205 				} else {
206 					m_CPx1 = m_x1 + m_x - m_x0;
207 					m_CPy1 = m_y1 + m_y - m_y0;
208 				}
209 				arrow->SetControlPoints (m_CPx0, m_CPy0, m_CPx1, m_CPy1, m_CPx2, m_CPy2, m_CPx3, m_CPy3);
210 			}
211 		return;
212 	}
213 	gccv::Item *item = m_pView->GetCanvas ()->GetItemAt (m_x, m_y);
214 	if (item) {
215 		gcu::Object *cur = (item == m_Item)? m_Target: dynamic_cast <gcu::Object *> (item->GetClient ());
216 		if (!cur) { // looks that this can happen, why?
217 			arrow->SetControlPoints (0., 0., 0., 0., 0., 0., 0., 0.);
218 			return;
219 		}
220 		if (cur->GetType () == gcu::FragmentType)
221 			cur = static_cast <gcp::Fragment *> (cur)->GetAtom ();
222 		if (cur == m_pObject) {
223 			switch (m_pObject->GetType ()) {
224 			case gcu::BondType: {
225 				gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
226 				if (!m_Target || m_Target == bond->GetAtom (0) || m_Target == bond->GetAtom (1)) {
227 					m_Target = NULL;
228 					BondToAdjAtom ();
229 					return;
230 				} else {
231 					m_Target = NULL;
232 					break; // TODO: implement
233 				}
234 				break;
235 			}
236 			case gcu::AtomType: {
237 				gcp::Atom *atom = static_cast <gcp::Atom *> (m_pObject);
238 				if (!m_Target) {
239 					std::map< gcu::Atom *, gcu::Bond * >::iterator it;
240 					double x, y;
241 					atom->GetCoords (&x, &y);
242 					x *= pTheme->GetZoomFactor ();
243 					y *= pTheme->GetZoomFactor ();
244 					double a0 = (x == m_x && y == m_y)? go_nan: atan2 (y - m_y, m_x - x);
245 					if (isnan (a0))
246 						m_Target = atom->GetFirstBond (it);
247 					else
248 						m_Target = atom->GetBondAtAngle (a0);
249 					if (m_Target && AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
250 						AtomToAdjBond ();
251 					else
252 						m_Target = NULL;
253 					break;
254 				} else {
255 					gcp::Bond *bond = dynamic_cast <gcp::Bond *> (m_Target);
256 					if (bond && AllowAsTarget (bond))
257 						AtomToAdjBond ();
258 					else
259 						m_Target = NULL;
260 				}
261 				break;
262 			}
263 			default:
264 				if (m_pObject->GetType () == gcp::ElectronType) {
265 					gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
266 					gcu::Object *obj = m_pObject->GetParent ();
267 					gcp::Atom *atom = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
268 					double x, a0;
269 					elec->GetPosition (&a0, &x);
270 					a0 *= M_PI / 180.;
271 					// find the most probable bond or the nearest atom
272 					if (atom->GetBondsNumber () > 0) {
273 						m_Target = atom->GetBondAtAngle (a0);
274 						if (!AllowAsTarget (static_cast <gcp::Bond *> (m_Target)))
275 							m_Target = NULL;
276 						if (m_Target == NULL)
277 							break;
278 						ElectronToAdjBond ();
279 					}
280 					break;
281 				} else if (m_pObject->GetType () == gcp::MechanismArrowType) {
282 					break; // TODO: implement
283 				} else {
284 					m_Target = NULL;
285 					break;	// TODO: add more types
286 				}
287 			}
288 		} else if (cur == m_Target) {
289 			gcu::TypeId sid, tid;
290 			sid = m_pObject->GetType ();
291 			tid = m_Target->GetType ();
292 			switch (sid) {
293 			case gcu::BondType:
294 				switch (tid) {
295 				case gcu::BondType:
296 					return; // nothing should change in that case
297 				case gcu::AtomType: {
298 					gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
299 					if (m_Target == bond->GetAtom (0) || m_Target == bond->GetAtom (1))
300 						BondToAdjAtom ();
301 					break;
302 				}
303 				default:
304 					break;
305 				}
306 				break;
307 			case gcu::AtomType:
308 				break;
309 			default:
310 				break;
311 			}
312 			return;
313 		} else {
314 			switch (cur->GetType ()) {
315 			case gcu::BondType: {
316 				gcp::Bond *bond = static_cast <gcp::Bond *> (cur);
317 				if (!AllowAsTarget (bond)) {
318 					m_Target = NULL;
319 					break;
320 				}
321 				m_Target = bond;
322 				switch (m_pObject->GetType ()) {
323 				case gcu::BondType: {
324 					BondToAdjBond ();
325 					return;
326 				}
327 				case gcu::AtomType: {
328 					AtomToAdjBond ();
329 					return;
330 				}
331 				default:
332 					if (m_pObject->GetType () == gcp::ElectronType) {
333 						ElectronToAdjBond ();
334 						return;
335 					}
336 					m_Target = NULL;
337 					break;
338 				}
339 				break;
340 			}
341 			case gcu::AtomType: {
342 				gcp::Atom *atom = static_cast <gcp::Atom *> (cur);
343 				if (!AllowAsTarget (atom)) {
344 					m_Target = NULL;
345 					break;
346 				}
347 				m_Target = cur;
348 				switch (m_pObject->GetType ()) {
349 				case gcu::BondType: {
350 					gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
351 					if (cur == bond->GetAtom (0) || cur == bond->GetAtom (1))
352 						BondToAdjAtom ();
353 					else
354 						BondToAtom ();
355 					break;
356 				}
357 				case gcu::AtomType:
358 					AtomToAtom ();
359 					break;
360 				default:
361 					if (m_pObject->GetType () == gcp::ElectronType) {
362 						ElectronToAtom ();
363 						return;
364 					}
365 					m_Target = NULL;
366 				}
367 				break;
368 			}
369 			default:
370 				m_Target = NULL;
371 				break;	// TODO: add more types
372 			}
373 		}
374 	} else
375 		m_Target = NULL;
376 	if (m_Target == NULL)
377 		arrow->SetControlPoints (0., 0., 0., 0., 0., 0., 0., 0.);
378 }
379 
OnMotion()380 void gcpCurvedArrowTool::OnMotion ()
381 {
382 	m_pData->UnselectAll ();
383 	bool allowed = false;
384 	if (m_pObject)
385 		switch (m_pObject->GetType ()) {
386 		case gcu::FragmentType:
387 			m_pObject = static_cast <gcp::Fragment *> (m_pObject)->GetAtom ();
388 		case gcu::AtomType:
389 			allowed = AllowAsSource (reinterpret_cast <gcp::Atom *> (m_pObject));
390 			break;
391 		case gcu::BondType:
392 			allowed = AllowAsSource (reinterpret_cast <gcp::Bond *> (m_pObject));
393 			break;
394 		default:
395 			if (m_pObject->GetType () == gcp::ElectronType) {
396 				if (m_Full)
397 					allowed = static_cast <gcp::Electron *> (m_pObject)->IsPair ();
398 				else
399 					allowed = true;
400 			} else if (m_pObject->GetType () == gcp::MechanismArrowType) {
401 				if (m_Full)
402 					allowed = static_cast <gcp::MechanismArrow *> (m_pObject)->GetPair ();
403 				else
404 					allowed = !static_cast <gcp::MechanismArrow *> (m_pObject)->GetPair ();
405 				if (allowed)
406 					static_cast <gccv::BezierArrow *> (dynamic_cast <gccv::ItemClient *> (m_pObject)->GetItem ())->SetShowControls (true);
407 			}
408 			break;
409 		}
410 	if (allowed)
411 		m_pData->SetSelected (m_pObject);
412 	gdk_window_set_cursor (gtk_widget_get_parent_window (m_pWidget), allowed? m_pApp->GetCursor (gcp::CursorPencil): m_pApp->GetCursor (gcp::CursorUnallowed));
413 }
414 
OnLeaveNotify()415 void gcpCurvedArrowTool::OnLeaveNotify ()
416 {
417 	m_pData->UnselectAll ();
418 }
419 
OnRelease()420 void gcpCurvedArrowTool::OnRelease ()
421 {
422 	m_pApp->ClearStatus ();
423 	gcp::Document* pDoc = m_pView->GetDoc ();
424 	if (m_Item) {
425 		if (m_pObject->GetType () == gcp::MechanismArrowType) {
426 			m_Item = NULL;
427 			gcp::Operation *op = pDoc->GetNewOperation (gcp::GCP_MODIFY_OPERATION);
428 			gcu::Object *obj = m_pObject->GetGroup ();
429 			op->AddObject (obj, 0);
430 			gcp::MechanismArrow *a = static_cast <gcp::MechanismArrow *> (m_pObject);
431 			if (m_Target)
432 				a->SetControlPoint (2, (m_CPx2 - m_CPx3) / m_dZoomFactor, (m_CPy2 - m_CPy3) / m_dZoomFactor);
433 			else
434 				a->SetControlPoint (1, (m_CPx1 - m_CPx0) / m_dZoomFactor, (m_CPy1 - m_CPy0) / m_dZoomFactor);
435 			m_pView->Update (m_pObject);
436 			op->AddObject (obj, 1);
437 			pDoc->FinishOperation ();
438 			return;
439 		} else {
440 			delete m_Item;
441 			m_Item = NULL;
442 		}
443 	}
444 	else
445 		return;
446 	if (!m_pObject || !m_Target || (m_CPx2 == 0. && m_CPy2 == 0.))
447 		return;
448 	gcp::Operation *op = pDoc->GetNewOperation (gcp::GCP_MODIFY_OPERATION);
449 	gcu::Object *obj = m_pObject->GetGroup ();
450 	op->AddObject (obj, 0);
451 	if (obj != m_Target->GetGroup ())
452 		op->AddObject (m_Target->GetGroup (), 0);
453 	gcp::MechanismArrow *a = new gcp::MechanismArrow ();
454 	gcp::Molecule *mol = static_cast <gcp::Molecule *> (m_Target->GetMolecule ());
455 	// we suppose that the molecule is owned either by a mechanism step, a reaction step or the document
456 	// anyway, the tool must refuse all other situations
457 	obj = mol->GetParent ();
458 	if (obj->GetType () == gcu::ReactantType)
459 		obj = obj->GetParent ();
460 	if (obj->GetType () == gcu::DocumentType) {
461 		gcp::Molecule *mol_ = static_cast <gcp::Molecule *> (m_pObject->GetMolecule ());
462 		if (mol_->GetParent () != obj) {// the source is already inside a mechanism step, NOT in a reaction step
463 			obj = mol_->GetParent ();
464 			obj->AddChild (mol);
465 		} else {
466 			gcp::MechanismStep *step = new gcp::MechanismStep ();
467 			pDoc->AddChild (step);
468 			obj = step;
469 			obj->AddChild (mol);
470 			if (mol !=mol_)
471 				obj->AddChild (mol_);
472 		}
473 	}
474 	pDoc->AddObject (a);
475 	obj->AddChild (a);
476 	a->SetSource (m_pObject);
477 	a->SetSourceAux (m_SourceAux);
478 	a->SetTarget (m_Target);
479 	a->SetPair (m_Full);
480 	a->SetControlPoint (1, m_CPx1 / m_dZoomFactor, m_CPy1 / m_dZoomFactor);
481 	a->SetControlPoint (2, m_CPx2 / m_dZoomFactor, m_CPy2 / m_dZoomFactor);
482 	if (m_SetEnd)
483 		a->SetEndAtNewBondCenter (m_EndAtBondCenter);
484 	a->EmitSignal (gcp::OnChangedSignal);
485 	m_pView->Update (a);
486 
487 	gcu::Object *group = obj->GetGroup ();
488 	op->AddObject ((group)? group: obj, 1);
489 	pDoc->FinishOperation ();
490 }
491 
on_default(GtkToggleButton * button)492 static void on_default (GtkToggleButton *button)
493 {
494 	GOConfNode *node = go_conf_get_node (gcu::Application::GetConfDir (), "paint/plugins/arrows");
495 	go_conf_set_bool (node, "end-at-new-bond-center", gtk_toggle_button_get_active (button));
496 	go_conf_free_node (node);
497 }
498 
on_end_toggled(GtkToggleButton * button,gcpCurvedArrowTool * tool)499 static void on_end_toggled (GtkToggleButton *button, gcpCurvedArrowTool *tool)
500 {
501 	tool->SetEndAtBondCenter (gtk_toggle_button_get_active (button));
502 }
503 
GetPropertyPage()504 GtkWidget *gcpCurvedArrowTool::GetPropertyPage ()
505 {
506 	if (!m_Full)
507 		return NULL;
508 	gcugtk::UIBuilder *builder = new gcugtk::UIBuilder (UIDIR"/curvedarrowtool.ui", GETTEXT_PACKAGE);
509 	GtkWidget *b = builder->GetWidget ("target-btn");
510 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), m_EndAtBondCenter);
511 	g_signal_connect (G_OBJECT (b), "toggled", G_CALLBACK (on_end_toggled), this);
512 	GtkWidget *w = builder->GetWidget ("default");
513 	g_signal_connect_swapped (w, "clicked", G_CALLBACK (on_default), b);
514 	GtkWidget *res = builder->GetRefdWidget ("curved-arrow-grid");
515 	delete builder;
516 	return res;
517 }
518 
AllowAsSource(gcp::Atom * atom)519 bool gcpCurvedArrowTool::AllowAsSource (gcp::Atom *atom)
520 {
521 	if (!atom->HasAvailableElectrons (m_Full))
522 	    return false;
523 	std::set <gcu::Object *>::iterator i;
524 	gcu::Object* obj = atom->GetFirstLink (i);
525 	while (obj && obj->GetType () != gcp::MechanismArrowType)
526 		obj = atom->GetNextLink (i);
527 	if (obj) {
528 		if (m_Full)
529 			return false;
530 		// for homolytic cleavage, the bond must be the source for both arrows
531 		gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
532 		if (arrow->GetPair ())
533 			return false;
534 		// only one arrow should be there
535 		obj = atom->GetNextLink (i);
536 		if (obj && obj->GetType () == gcp::MechanismArrowType)
537 			return false;
538 	}
539 	return true;
540 }
541 
AllowAsTarget(gcp::Atom * atom)542 bool gcpCurvedArrowTool::AllowAsTarget (gcp::Atom *atom)
543 {
544 	if (atom == m_pObject || atom == m_pObject->GetParent ())
545 		return false;
546 	if (m_pObject->GetType () == gcu::AtomType && static_cast <gcp::Atom *> (m_pObject)->GetBond (atom))
547 			return false;
548 	if (m_pObject->GetType () == gcp::ElectronType) {
549 		gcu::Object *obj = m_pObject->GetParent ();
550 		gcp::Atom *atom0 = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
551 		if (atom0->GetBond (atom))
552 			return false;
553 	}
554 	if (m_pObject->GetType () == gcu::BondType && !m_Full) {
555 		// check if there is any existant arrow with the same source and target
556 		std::set <gcu::Object *>::iterator i;
557 		gcu::Object* obj = atom->GetFirstLink (i);
558 		while (obj && obj->GetType () != gcp::MechanismArrowType)
559 			obj = atom->GetNextLink (i);
560 		if (obj && static_cast <gcp::MechanismArrow *> (obj)->GetSource () == m_pObject
561 		    && static_cast <gcp::MechanismArrow *> (obj)->GetTarget () == atom)
562 			return false;
563 	}
564 	// now check that molecules are not in incompatible groups
565 	gcu::Object *obj1 = m_pObject->GetMolecule (), *obj2 = atom->GetMolecule ();
566 	if (obj1 != obj2) {
567 		// otherwise, no problem
568 		// get molecules parents
569 		obj1 = obj1->GetParent ();
570 		obj2 = obj2->GetParent ();
571 		// if they belong to two different reaction step, return false
572 		if ((obj1->GetType () == gcp::ReactionStepType || obj2->GetType () == gcp::ReactionStepType) && obj1 != obj2)
573 			return false;
574 		// no arrows between a mesomer and anything else
575 		if (obj1->GetType () == gcp::MesomerType || obj2->GetType () == gcp::MesomerType)
576 			return false;
577 		// otherwise we request that the two molecules have the same parent or parents inside one another (might be a bug)
578 		if (obj1 != obj2 && obj1->GetParent () != obj2->GetParent () && obj1->GetParent () != obj2 && obj1 != obj2->GetParent ())
579 			return false;
580 	}
581 	return atom->AcceptNewBonds () || atom->GetBondsNumber ();
582 }
583 
AllowAsSource(gcp::Bond * bond)584 bool gcpCurvedArrowTool::AllowAsSource (gcp::Bond *bond)
585 {
586 	std::set <gcu::Object *>::iterator i;
587 	gcu::Object* obj = bond->GetFirstLink (i);
588 	while (obj && obj->GetType () != gcp::MechanismArrowType)
589 		obj = bond->GetNextLink (i);
590 	if (obj) {
591 		if (m_Full)
592 			return false;
593 		// for homolytic cleavage, the bond must be the source for both arrows
594 		gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
595 		if (arrow->GetPair () || arrow->GetSource () != bond)
596 			return false;
597 		// only one arrow should be there
598 		obj = bond->GetNextLink (i);
599 		if (obj && obj->GetType () == gcp::MechanismArrowType)
600 			return false;
601 	}
602 	return true;
603 }
604 
AllowAsTarget(gcp::Bond * bond)605 bool gcpCurvedArrowTool::AllowAsTarget (gcp::Bond *bond)
606 {
607 	std::set <gcu::Object *>::iterator i;
608 	gcu::Object* obj = bond->GetFirstLink (i);
609 	while (obj && obj->GetType () != gcp::MechanismArrowType)
610 		obj = bond->GetNextLink (i);
611 	if (obj) {
612 		if (m_Full)
613 			return false;
614 		// for homolytic cleavage, the bond must be the source for both arrows
615 		gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
616 		if (arrow->GetPair () || arrow->GetTarget () != bond || arrow->GetSource () == m_pObject)
617 			return false;
618 		// only one arrow should be there
619 		obj = bond->GetNextLink (i);
620 		if (obj && obj->GetType () == gcp::MechanismArrowType)
621 			return false;
622 	}
623 	// now check if the source is an adjacent bond or atom
624 	switch (m_pObject->GetType ()) {
625 	case gcu::BondType: {
626 		gcp::Bond *bond0 = static_cast <gcp::Bond *> (m_pObject);
627 		if (bond->GetAtom (bond0->GetAtom (0)) == NULL && bond->GetAtom (bond0->GetAtom (1)) == NULL)
628 			return false;
629 		break;
630 	}
631 	case gcu::AtomType: {
632 		gcp::Atom *atom = static_cast <gcp::Atom *> (m_pObject);
633 		if (bond->GetAtom (0) != atom && bond->GetAtom (1) != atom)
634 			return false;
635 		break;
636 	}
637 	default:
638 		if (m_pObject->GetType () == gcp::ElectronType) {
639 			gcu::Object *obj = m_pObject->GetParent ();
640 			gcp::Atom *atom = static_cast <gcp::Atom *> ((obj->GetType () == gcu::AtomType)? obj: static_cast <gcp::Fragment *> (m_pObject)->GetAtom ());
641 			if (bond->GetAtom (0) != atom && bond->GetAtom (1) != atom)
642 				return false;
643 			break;
644 		}
645 		return false;
646 	}
647 	return true;
648 }
649 
AllowAsSource(gcp::Electron * elec)650 bool gcpCurvedArrowTool::AllowAsSource (gcp::Electron *elec)
651 {
652 	if (m_Full && !elec->IsPair ())
653 		return false;
654 	std::set <gcu::Object *>::iterator i;
655 	gcu::Object* obj = elec->GetFirstLink (i);
656 	while (obj && obj->GetType () != gcp::MechanismArrowType)
657 		obj = elec->GetNextLink (i);
658 	if (obj) {
659 		if (m_Full)
660 			return false;
661 		// for homolytic cleavage, the electron must be the source for both arrows
662 		gcp::MechanismArrow *arrow = static_cast <gcp::MechanismArrow *> (obj);
663 		if (arrow->GetPair ())
664 			return false;
665 		// only one arrow should be there
666 		obj = elec->GetNextLink (i);
667 		if (obj && obj->GetType () == gcp::MechanismArrowType)
668 			return false;
669 	}
670 	return true;
671 }
672 
AtomToAdjBond()673 void gcpCurvedArrowTool::AtomToAdjBond ()
674 {
675 	double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy, s = 1.;
676 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
677 	gcp::Bond *bond = static_cast <gcp::Bond *> (m_Target);
678 	gcp::Atom *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
679 			  *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
680 	if (end == m_pObject) {
681 		end = start;
682 		start = static_cast <gcp::Atom *> (m_pObject);
683 	}
684 	start->GetCoords (&x0, &y0);
685 	end->GetCoords (&x1, &y1);
686 	// convert to canvas coordinates
687 	x0 *= m_dZoomFactor;
688 	y0 *= m_dZoomFactor;
689 	x1 *= m_dZoomFactor;
690 	y1 *= m_dZoomFactor;
691 	dx = y1 - y0;
692 	dy = x0 - x1;
693 	l = hypot (dx, dy);
694 	dx /= l;
695 	dy /= l;
696 	// try to find on which side we are
697 	if ((m_x - x0) * dx + (m_y - y0) * dy < 0) {
698 		dx = -dx;
699 		dy = -dy;
700 		s = -1;
701 	}
702 	x3 = ((x0 + x1) / 2 + dx * pTheme->GetPadding ());
703 	y3 = ((y0 + y1) / 2 + dy * pTheme->GetPadding ());
704 	x3 /= m_dZoomFactor;
705 	y3 /= m_dZoomFactor;
706 	bond->AdjustPosition (x3, y3);
707 	x3 *= m_dZoomFactor;
708 	y3 *= m_dZoomFactor;
709 	l /= 2.;
710 	m_CPx1 = m_CPx2 = l * dx;
711 	m_CPy1 = m_CPy2 = l * dy;
712 	double a = atan2 (-m_CPy1, m_CPx1) * 180. / M_PI;
713 	if (start->GetPosition (a, x0, y0)) {
714 		// convert to canvas coordinates
715 		x0 *= m_dZoomFactor;
716 		y0 *= m_dZoomFactor;
717 		if (!m_Full) {
718 			x0 -= 2. * dy * s;
719 			y0 += 2. * dx * s;
720 			x3 += 2. * dy * s;
721 			y3 -= 2. * dx * s;
722 		}
723 		x1 = x0 + m_CPx1;
724 		y1 = y0 + m_CPy1;
725 		x2 = x3 + m_CPx1;
726 		y2 = y3 + m_CPy1;
727 		m_CPx0 = x0;
728 		m_CPy0 = y0;
729 		static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
730 	} else
731 		x0 = y0 = m_CPx1 = m_CPx2 = m_CPy1 = m_CPy2 = x3 = y3 = 0;
732 	m_SetEnd = false;
733 	static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
734 }
735 
AtomToAtom()736 void gcpCurvedArrowTool::AtomToAtom ()
737 {
738 	double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy;
739 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
740 	gcp::Atom *start = static_cast <gcp::Atom *> (m_pObject),
741 			  *end = static_cast <gcp::Atom *> (m_Target);
742 	start->GetCoords (&x0, &y0);
743 	end->GetCoords (&x3, &y3);
744 	x0 *= m_dZoomFactor;
745 	y0 *= m_dZoomFactor;
746 	x3 *= m_dZoomFactor;
747 	y3 *= m_dZoomFactor;
748 	dx = x3 - x0;
749 	dy = y3 - y0;
750 	l = hypot (dx, dy);
751 	dx /= l;
752 	dy /= l;
753 	l = pTheme->GetBondLength () * m_dZoomFactor;
754 	if (start->GetBondsNumber () == 0) {
755 		// Set values to m_CPx1 and m_CPy1 according to the cursor position
756 		if ((m_x - x0) * (y3 - y0) - (m_y - y0) * (x3 - x0) < 0)
757 		{
758 			m_CPx1 = -l * dy;
759 			m_CPy1 = l * dx;
760 		} else {
761 			m_CPx1 = l * dy;
762 			m_CPy1 = -l * dx;
763 		}
764 	}
765 	double angle = -atan2 (m_CPy1, m_CPx1) * 180. / M_PI;
766 	if (start->GetPosition (angle, x0, y0)) {
767 		// convert to canvas coordinates
768 		m_CPx0 = x0 *= m_dZoomFactor;
769 		m_CPy0 = y0 *= m_dZoomFactor;
770 		x1 = x0 + m_CPx1;
771 		y1 = y0 + m_CPy1;
772 		if (!m_Full || m_EndAtBondCenter) {
773 			x3 = (x3 + x0) / 2.;
774 			y3 = (y3 + y0) / 2.;
775 			if (!m_Full) {
776 				x3 -= 2. * dx;
777 				y3 -= 2. * dy;
778 			}
779 			if (m_CPx1 * dy - m_CPy1 * dx < 0.) {
780 				m_CPx2 = -dy * l;
781 				m_CPy2 = dx * l;
782 			} else {
783 				m_CPx2 = dy * l;
784 				m_CPy2 = -dx * l;
785 			}
786 		} else {
787 			angle = -atan2 (m_CPy2, m_CPx2) * 180. / M_PI;
788 			if (end->GetPosition (angle, x3, y3)) {
789 				x3 *= m_dZoomFactor;
790 				y3 *= m_dZoomFactor;
791 				m_CPx2 = -dx * l;
792 				m_CPy2 = -dy * l;
793 			} else
794 				goto ata_err;
795 		}
796 		x2 = x3 + m_CPx2;
797 		y2 = y3 + m_CPy2;
798 		static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
799 	} else
800 ata_err:
801 		x0 = y0 = m_CPx1 = m_CPx2= m_CPy0 = m_CPy1 = x3 = y3 = 0;
802 	m_SetEnd = m_Full;
803 	static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
804 }
805 
BondToAdjAtom()806 void gcpCurvedArrowTool::BondToAdjAtom ()
807 {
808 	double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy, s = 1.;
809 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
810 	gcp::Bond *bond = static_cast <gcp::Bond *> (m_pObject);
811 	gcp::Atom *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
812 			  *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
813 	if (start == m_Target) {
814 		start = end;
815 		end = static_cast <gcp::Atom *> (m_Target);
816 	} else if (m_Target && end != m_Target)
817 		return; // should not occur
818 	start->GetCoords (&x0, &y0);
819 	end->GetCoords (&x1, &y1);
820 	// convert to canvas coordinates
821 	x0 *= m_dZoomFactor;
822 	y0 *= m_dZoomFactor;
823 	x1 *= m_dZoomFactor;
824 	y1 *= m_dZoomFactor;
825 	if (!m_Target) {
826 		// use the atom nearest to the mouse pointer
827 		x2 = hypot (x0 - m_x, y0 - m_y);
828 		y2 = hypot (x1 - m_x, y1 - m_y);
829 		if (x2 < y2) {
830 			m_Target = start;
831 			x2 = x0;
832 			x0 = x1;
833 			x1 = x2;
834 			y2 = y0;
835 			y0 = y1;
836 			y1 = y2;
837 		} else
838 			m_Target = end;
839 	}
840 	if (AllowAsTarget (static_cast <gcp::Atom *> (m_Target))) {
841 		x2 = m_x - x0;
842 		y2 = m_y - y0;
843 		x1 -= x0;
844 		y1 -= y0;			// use x3 as bond length for now
845 		l = hypot (x1, y1);
846 		dx = x1 / l;
847 		dy = y1 / l;
848 		// everything being normalized, vector product sign will say on which side we are
849 		// and scalar product where we are. Let's use x3 for scalar and y3 for vector products.
850 		x2 /= l;
851 		y2 /= l;
852 		x3 = dx * x2 + dy * y2;
853 		y3 = dx * y2 - dy * x2;
854 		x0 += (x1 /= 2.);
855 		y0 += (y1 /= 2.);
856 		if (!m_Full) {
857 			x0 += 2. * dx;
858 			y0 += 2. * dy;
859 		}
860 		x2 = dx;
861 		if (y3 < 0) {
862 			dx = dy;
863 			dy = -x2;
864 		} else {
865 			dx = -dy;
866 			dy = x2;
867 			s = -1.;
868 		}
869 		// add some padding
870 		x0 += dx * pTheme->GetPadding ();
871 		y0 += dy * pTheme->GetPadding ();
872 		x0 /= m_dZoomFactor;
873 		y0 /= m_dZoomFactor;
874 		bond->AdjustPosition (x0, y0);
875 		m_CPx0 = x0 *= m_dZoomFactor;
876 		m_CPy0 = y0 *= m_dZoomFactor;
877 		l /= 2.;
878 		m_CPx1 = dx * l;
879 		m_CPy1 = dy * l;
880 		x1 = x0 + m_CPx1;
881 		y1 = y0 + m_CPy1;
882 		// adjust end position
883 		double angle = -atan2 (dy, dx) * 180. / M_PI;
884 		if (static_cast <gcp::Atom *> (m_Target)->GetPosition (angle, x3, y3)) {
885 			// convert to canvas coordinates
886 			x3 *= m_dZoomFactor;
887 			y3 *= m_dZoomFactor;
888 			// set second control point
889 			l += pTheme->GetArrowHeadA ();
890 			m_CPx2 = dx * l;
891 			m_CPy2 = dy * l;
892 			if (!m_Full) {
893 				x3 += 2. * dy * s;
894 				y3 -= 2. * dx * s;
895 			}
896 			x2 = x3 + m_CPx2;
897 			y2 = y3 + m_CPy2;
898 			m_LastTarget = m_Target;
899 			static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
900 		} else
901 			x0 = y0 = x1 = y1 = x2 = y2 = x3 = y3 = m_CPx2 = m_CPy2 = 0;
902 	} else
903 		m_Target = NULL;
904 	m_SetEnd = false;
905 	static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
906 }
907 
BondToAdjBond()908 void gcpCurvedArrowTool::BondToAdjBond ()
909 {
910 	double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., l, dx, dy;
911 				double x, y, x_, y_;
912 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
913 	gcp::Bond *bond = static_cast <gcp::Bond *> (m_Target);
914 	gcp::Atom *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
915 			  *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
916 	start->GetCoords (&x, &y);
917 	end->GetCoords (&x_, &y_);
918 	// convert to canvas coordinates
919 	x *= m_dZoomFactor;
920 	y *= m_dZoomFactor;
921 	x_ *= m_dZoomFactor;
922 	y_ *= m_dZoomFactor;
923 	if (static_cast <gcp::Bond *> (m_pObject)->GetAtom (start) == NULL) {
924 		gcp::Atom *buf = start;
925 		start = end;
926 		end = buf;
927 		// also exchange coordinates
928 		x0 = x;
929 		x = x_;
930 		x_ = x0;
931 		x0 = y;
932 		y = y_;
933 		y_ = x0;
934 	}
935 	// start is the common atom, x and y its coordinates
936 	x0 = m_CPx0;
937 	y0 = m_CPy0;
938 	x1 = x0 + m_CPx1;
939 	y1 = y0 + m_CPy1;
940 	x3 = (x + x_) / 2.;
941 	y3 = (y + y_) / 2.;
942 	dx = y_ - y;
943 	dy = x - x_;
944 	l = hypot (dx, dy);
945 	dx /= l;
946 	dy /= l;
947 	if (!m_Full) {
948 		x3 += 2. * dy;
949 		y3 -= 2. * dx;
950 	}
951 	if ((m_CPx1 * (y0 - y) - m_CPy1 * (x0 - x)) * (dx * (y3 - y) - dy * (x3 - x)) > 0.) {
952 		dx = -dx;
953 		dy = -dy;
954 	}
955 	x3 += dx * pTheme->GetPadding ();
956 	y3 += dy * pTheme->GetPadding ();
957 	x3 /= m_dZoomFactor;
958 	y3 /= m_dZoomFactor;
959 	bond->AdjustPosition (x3, y3);
960 	x3 *= m_dZoomFactor;
961 	y3 *= m_dZoomFactor;
962 	l /= 2.;
963 	l += pTheme->GetArrowHeadA ();
964 	m_CPx2 = dx * l;
965 	m_CPy2 = dy * l;
966 	x2 = x3 + m_CPx2;
967 	y2 = y3 + m_CPy2;
968 	m_SourceAux = NULL;
969 	m_SetEnd = false;
970 	static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
971 	static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
972 }
973 
BondToAtom()974 void gcpCurvedArrowTool::BondToAtom ()
975 {
976 	double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., x, y, a, dx, dy;
977 	gcp::Atom *start = static_cast <gcp::Atom *> (m_LastTarget),
978 			  *end = static_cast <gcp::Atom *> (m_Target);
979 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
980 	start->GetCoords (&x0, &y0);
981 	end->GetCoords (&x, &y);
982 	x0 *= m_dZoomFactor;
983 	y0 *= m_dZoomFactor;
984 	x *= m_dZoomFactor;
985 	y *= m_dZoomFactor;
986 	dx = x - x0;
987 	dy = y - y0;
988 	if (!m_Full || m_EndAtBondCenter) {
989 		double l = hypot (dx, dy);
990 		dx /= l;
991 		dy /= l;
992 		x3 = (x + x0) / 2.;
993 		y3 = (y + y0) / 2.;
994 		if (!m_Full) {
995 			x3 -= 2. * dx;
996 			y3 -= 2. * dy;
997 		}
998 		if (m_CPx1 * dy - m_CPy1 * dx < 0.) {
999 			dx = -dx;
1000 			dy = -dy;
1001 		}
1002 		x2 = x3 + (m_CPx2 = dy * pTheme->GetBondLength () * m_dZoomFactor);
1003 		y2 = y3 + (m_CPy2 = -dx * pTheme->GetBondLength () * m_dZoomFactor);
1004 		x0 = m_CPx0;
1005 		y0 = m_CPy0;
1006 		x1 = x0 + m_CPx1;
1007 		y1 = y0 + m_CPy1;
1008 	} else {
1009 		a = atan2 (dy, -dx) * 180. / M_PI;
1010 		if (end->GetPosition (a, x3, y3)) {
1011 			x3 *= m_dZoomFactor;
1012 			y3 *= m_dZoomFactor;
1013 			x2 = (x0 + x) / 2.;
1014 			y2 = (y0 + y) / 2.;
1015 			m_CPx2 = x2 - x3;
1016 			m_CPy2 = y2 - y3;
1017 			x0 = m_CPx0;
1018 			y0 = m_CPy0;
1019 			x1 = x0 + m_CPx1;
1020 			y1 = y0 + m_CPy1;
1021 		} else
1022 			x0 = y0 = x1 = y1 = m_CPx2 = m_CPy2 = 0.;
1023 	}
1024 	m_SourceAux = m_LastTarget;
1025 	m_SetEnd = m_Full;
1026 	static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
1027 	static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
1028 }
1029 
ElectronToAdjBond()1030 void gcpCurvedArrowTool::ElectronToAdjBond ()
1031 {
1032 	gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
1033 	gcp::Bond *bond = static_cast <gcp::Bond *> (m_Target);
1034 	gcp::Atom *atom = static_cast <gcp::Atom *> (elec->GetParent ()),
1035 			  *start = static_cast <gcp::Atom *> (bond->GetAtom (0)),
1036 			  *end = static_cast <gcp::Atom *> (bond->GetAtom (1));
1037 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
1038 	if (end == atom) {
1039 		end = start;
1040 		start = atom;
1041 	}
1042 	double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., x, y, a, dx, dy, l;
1043 	elec->GetPosition (&a, &dx);
1044 	a *= M_PI / 180.;
1045 	if (dx != 0.) {
1046 		x = dx * cos (a);
1047 		y = -dx * sin (a);
1048 		x *= m_dZoomFactor;
1049 		y *= m_dZoomFactor;
1050 	} else {
1051 		start->GetRelativePosition (a * 180. / M_PI, x, y);
1052 		x *= m_dZoomFactor;
1053 		y *= m_dZoomFactor;
1054 		x += 2. * cos (a);
1055 		y -= 2. * sin (a);
1056 	}
1057 	start->GetCoords (&x0, &y0);
1058 	end->GetCoords (&x3, &y3);
1059 	x0 *= m_dZoomFactor;
1060 	y0 *= m_dZoomFactor;
1061 	x3 *= m_dZoomFactor;
1062 	y3 *= m_dZoomFactor;
1063 	dx = x3 - x0;
1064 	dy = y3 - y0;
1065 	l = hypot (x, y);
1066 	// store x and y in x1 and y1
1067 	x1 = x;
1068 	y1 = y;
1069 	x = x / l;
1070 	y = y / l;
1071 	l = pTheme->GetBondLength () * m_dZoomFactor / 2.;
1072 	m_CPx1 = x * l;
1073 	m_CPy1 = y * l;
1074 	l = hypot (dx, dy);
1075 	dx /= l;
1076 	dy /= l;
1077 	// try to find on which side we are
1078 	x3 = (x0 + x3) / 2.;
1079 	y3 = (y0 + y3) / 2.;
1080 	if (!m_Full) {
1081 		if (elec->IsPair ()) {
1082 			if ((y3 - y0) * x - (x3 - x0) * y < 0) {
1083 				x0 += y * 2.;
1084 				y0 -= x * 2.;
1085 			} else {
1086 				x0 -= y * 2.;
1087 				y0 += x * 2.;
1088 
1089 			}
1090 		}
1091 		x3 -= 2. * dx;
1092 		y3 -= 2. * dy;
1093 	}
1094 	x0 += x1 + pTheme->GetPadding () * cos (a);
1095 	y0 += y1 - pTheme->GetPadding () * sin (a);
1096 	x1 = x0 + m_CPx1;
1097 	y1 = y0 + m_CPy1;
1098 	if ((m_CPy1) * dx - (m_CPx1) * dy > 0) {
1099 		dx = -dx;
1100 		dy = -dy;
1101 	}
1102 	x3 += dy * pTheme->GetPadding ();
1103 	y3 += -dx * pTheme->GetPadding ();
1104 	x3 /= m_dZoomFactor;
1105 	y3 /= m_dZoomFactor;
1106 	bond->AdjustPosition (x3, y3);
1107 	x3 *= m_dZoomFactor;
1108 	y3 *= m_dZoomFactor;
1109 	m_CPx2 = l * dy;
1110 	m_CPy2 = -l * dx;
1111 	x2 = x3 + m_CPx2;
1112 	y2 = y3 + m_CPy2;
1113 	static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
1114 	static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
1115 }
1116 
ElectronToAtom()1117 void gcpCurvedArrowTool::ElectronToAtom ()
1118 {
1119 	double x0 = 0., y0 = 0., x1 = 0., y1 = 0., x2 = 0., y2 = 0., x3 = 0., y3 = 0., x, y, a, dx, dy, l;
1120 	gcp::Electron *elec = static_cast <gcp::Electron *> (m_pObject);
1121 	gcp::Atom *start = static_cast <gcp::Atom *> (elec->GetParent ()),
1122 			  *end = static_cast <gcp::Atom *> (m_Target);
1123 	gcp::Theme *pTheme = m_pView->GetDoc ()->GetTheme ();
1124 	elec->GetPosition (&a, &dx);
1125 	a *= M_PI / 180.;
1126 	if (dx != 0.) {
1127 		x = dx * cos (a);
1128 		y = -dx * sin (a);
1129 		x *= m_dZoomFactor;
1130 		y *= m_dZoomFactor;
1131 	} else {
1132 		start->GetRelativePosition (a * 180. / M_PI, x, y);
1133 		x *= m_dZoomFactor;
1134 		y *= m_dZoomFactor;
1135 		x += 2. * cos (a);
1136 		y -= 2. * sin (a);
1137 	}
1138 	start->GetCoords (&x0, &y0);
1139 	end->GetCoords (&x3, &y3);
1140 	x0 *= m_dZoomFactor;
1141 	y0 *= m_dZoomFactor;
1142 	x3 *= m_dZoomFactor;
1143 	y3 *= m_dZoomFactor;
1144 	dx = x3 - x0;
1145 	dy = y3 - y0;
1146 	x0 += x + pTheme->GetPadding () * cos (a);
1147 	y0 += y - pTheme->GetPadding () * sin (a);
1148 	l = hypot (x, y) / pTheme->GetBondLength () / m_dZoomFactor * 2.;
1149 	x1 = x0 + (m_CPx1 = x / l);
1150 	y1 = y0 + (m_CPy1 = y / l);
1151 	l = hypot (dx, dy);
1152 	dx /= l;
1153 	dy /= l;
1154 	// try to find on which side we are
1155 	if ((m_CPy1) * dx - (m_CPx1) * dy > 0) {
1156 		dx = -dx;
1157 		dy = -dy;
1158 	}
1159 	if (!m_Full || m_EndAtBondCenter) {
1160 		x3 = (x3 + x0) / 2.;
1161 		y3 = (y3 + y0) / 2.;
1162 		if (!m_Full) {
1163 			x3 -= 2. * dx;
1164 			y3 -= 2. * dy;
1165 		}
1166 		x2 = x3 + (m_CPx2 = dy * pTheme->GetBondLength () * m_dZoomFactor);
1167 		y2 = y3 + (m_CPy2 = -dx * pTheme->GetBondLength () * m_dZoomFactor);
1168 	} else {
1169 		a = atan2 (dy, -dx) * 180. / M_PI;
1170 		x2 = (x0 + x3) / 2.;
1171 		y2 = (y0 + y3) / 2.;
1172 		if (end->GetPosition (a, x3, y3)) {
1173 			x3 *= m_dZoomFactor;
1174 			y3 *= m_dZoomFactor;
1175 			m_CPx2 = x2 - x3;
1176 			m_CPy2 = y2 - y3;
1177 		} else
1178 			x0 = y0 = x1 = y1 = m_CPx2 = m_CPy2 = 0.;
1179 	}
1180 	static_cast <gccv::BezierArrow *> (m_Item)->SetHead (m_Full? gccv::ArrowHeadFull: ((x2 -x3) * (y1 - y3) - (x1 - x3) * (y2 - y3) < 0? gccv::ArrowHeadRight: gccv::ArrowHeadLeft));
1181 	static_cast <gccv::BezierArrow *> (m_Item)->SetControlPoints (x0, y0, x1, y1, x2, y2, x3, y3);
1182 }
1183