// -*- C++ -*-
/*
* GChemPaint cycles plugin
* cycletool.cc
*
* Copyright (C) 2001-2007 Jean Bréfort <jean.brefort@normalesup.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "gchempaint-config.h"
#include "cycletool.h"
#include "lib/settings.h"
#include "lib/document.h"
#include "lib/application.h"
#include "lib/theme.h"
#include <glib/gi18n-lib.h>
#include <cmath>
#include <list>
#include <vector>
using namespace std;
static char* ToolNames [] = {
(char*) "Cycle3",
(char*) "Cycle4",
(char*) "Cycle5",
(char*) "Cycle6",
(char*) "Cycle7",
(char*) "Cycle8",
(char*) "CycleN",
};
gcpCycleTool::gcpCycleTool(gcpApplication *App, unsigned char size): gcpTool(App, ToolNames[size - 3])
{
if ((m_size = size)) Init();
else m_xn = NULL;
m_Chain = NULL;
}
gcpCycleTool::~gcpCycleTool()
{
if (m_size)
{
delete [] m_xn;
gnome_canvas_points_free(points);
}
if (m_Chain) delete m_Chain;
}
bool gcpCycleTool::OnClicked()
{
if (!m_size) return false;
double x1, y1, x2, y2;
double a0, a1, a2, b1, b2, m1, m2;
gcpAtom* pAtom, *pAtom1;
gcpBond* pBond, *pBond1;
gcpDocument *pDoc = m_pView->GetDoc ();
gcpTheme *pTheme = pDoc->GetTheme ();
m_dLength = pDoc->GetBondLength () * m_dZoomFactor;
map<Atom*, Bond*>::iterator j;
int i;
bool bDone = false;
m_dDefAngle = 0.0;
if (m_pObject)
{
bDone = true;
switch (m_pObject->GetType())
{
case BondType:
pBond = (gcpBond*) m_pObject;
pAtom = (gcpAtom*)pBond->GetAtom(0);
pAtom->GetCoords(&x1, &y1);
x1 *= m_dZoomFactor;
y1 *= m_dZoomFactor;
pAtom1 = (gcpAtom*)pBond->GetAtom(1);
pAtom1->GetCoords(&x2, &y2);
x2 *= m_dZoomFactor;
y2 *= m_dZoomFactor;
m_dLength = sqrt(square(x2 - x1) + square(y2 - y1));
if (pBond->IsCyclic() == 1)
{
gcpCycle *pCycle = NULL;
list<gcpCycle*>::iterator i;
pCycle = pBond->GetFirstCycle(i, pCycle);
a0 = atan2(y1 - y2, x2 - x1);
pCycle->GetAngles2D(pBond, &a1, &a2);
if (sin(a0 - a1) * sin (a0 - a2) > 0)
{
if (sin(a0 - a1) < 0.0)
{
points->coords[0] = m_xn[0] = m_x0 = x1;
points->coords[1] = m_xn[1] = m_y0 = y1;
m_dAngle = a0;
m_Start = pAtom;
m_End = pAtom1;
m_Direct = true;
}
else
{
points->coords[0] = m_xn[0] = m_x0 = x1 = x2;
points->coords[1] = m_xn[1] = m_y0 = y1 = y2;
m_dAngle = a0 - M_PI;
m_Start = pAtom1;
m_End = pAtom;
m_Direct = false;
}
}
}
else
{
m_Start = pAtom;
m_End = pAtom1;
a0 = pBond->GetAngle2DRad(pAtom);
b1 = a0 + m_dDev;
b2 = a0 - m_dDev;
m1 = m2 = M_PI;
pBond1 = (gcpBond*)pAtom->GetFirstBond(j);
while (pBond1)
{
if (pBond == pBond1)
{
pBond1 = (gcpBond*)pAtom->GetNextBond(j);
continue;
}
a1 = pBond1->GetAngle2DRad(pAtom);
a2 = fabs(b2 - a1);
if (a2 > M_PI) a2 = 2 * M_PI - a2;
if (m2 > a2) m2 = a2;
a1 = fabs(a1 - b1);
if (a1 > M_PI) a1 = 2 * M_PI - a1;
if (m1 > a1) m1 = a1;
pBond1 = (gcpBond*)pAtom->GetNextBond(j);
}
pBond1 = (gcpBond*)pAtom1->GetFirstBond(j);
a2 = b2;
b2 = - b1;
b1 = - b2;
while (pBond1)
{
if (pBond == pBond1)
{
pBond1 = (gcpBond*)pAtom1->GetNextBond(j);
continue;
}
a1 = pBond1->GetAngle2DRad(pAtom1);
a2 = fabs(b2 - a1);
if (a2 > M_PI) a2 = 2 * M_PI - a2;
if (m2 > a2) m2 = a2;
a1 = fabs(a1 - b1);
if (a1 > M_PI) a1 = 2 * M_PI - a1;
if (m1 > a1) m1 = a1;
pBond1 = (gcpBond*)pAtom1->GetNextBond(j);
}
if (m2 > m1)
{
points->coords[0] = m_xn[0] = m_x0 = x1;
points->coords[1] = m_xn[1] = m_y0 = y1;
m_dAngle = a0;
m_Direct = true;
}
else
{
points->coords[0] = m_xn[0] = m_x0 = x1 = x2;
points->coords[1] = m_xn[1] = m_y0 = y1 = y2;
m_dAngle = a0 - M_PI;
m_Direct = false;
}
}
if (m_Chain) delete m_Chain;
if (m_nState & GDK_SHIFT_MASK)
m_Chain = new gcpChain(pBond, m_Start);
break;
case AtomType:
pAtom = (gcpAtom*) m_pObject;
pAtom->GetCoords(&x1, &y1);
x1 *= m_dZoomFactor;
y1 *= m_dZoomFactor;
i = pAtom->GetBondsNumber();
switch (i)
{
case 0:
m_x0 = x1;
m_y0 = y1;
bDone = false;
break;
case 1:
pBond = (gcpBond*)pAtom->GetFirstBond(j);
pAtom1 = (gcpAtom*)pBond->GetAtom(pAtom);
a0 = pBond->GetAngle2DRad(pAtom);
points->coords[0] = m_xn[0] = m_x0 = x1;
points->coords[1] = m_xn[1] = m_y0 = y1;
m_dDefAngle = m_dAngle = - M_PI / 2 + a0 - m_dDev / 2;
break;
case 2:
pBond = (gcpBond*)pAtom->GetFirstBond(j);
pBond1 = (gcpBond*)pAtom->GetNextBond(j);
a1 = pBond->GetAngle2DRad(pAtom);
a2 = pBond1->GetAngle2DRad(pAtom);
a0 = (a1 + a2) / 2;
if (fabs(a1 - a2) > M_PI) a0 += M_PI;
points->coords[0] = m_xn[0] = m_x0 = x1;
points->coords[1] = m_xn[1] = m_y0 = y1;
m_dDefAngle = m_dAngle = - M_PI / 2 + a0 - m_dDev / 2;
break;
default:
vector<double> orientations;
orientations.reserve(i);
pBond = (gcpBond*)pAtom->GetFirstBond(j);
orientations.push_back(pBond->GetAngle2DRad(pAtom));
unsigned k;
while ((pBond = (gcpBond*)pAtom->GetNextBond(j)))
{
k = 0;
a0 = pBond->GetAngle2DRad(pAtom);
while ((a0 > orientations[k]) && (k < orientations.size())) k++;
orientations.insert(orientations.begin() + k, a0);
}
a0 = 2 * M_PI - orientations[i - 1] + orientations[0];
i = 0;
for (k = 1; k < orientations.size(); k++)
{
a1 = orientations[k] - orientations[k - 1];
if (a0 < a1)
{
a0 = a1;
i = k;
}
}
if (a0 < M_PI - m_dDev + M_PI / 18)
{
bDone = false;
break;
}
m_dAngle = (orientations[(i)? i - 1: orientations.size() - 1] + orientations[i] + M_PI - m_dDev) / 2;
if (!i) m_dAngle += M_PI;
m_dDefAngle = m_dAngle;
points->coords[0] = m_xn[0] = m_x0 = x1;
points->coords[1] = m_xn[1] = m_y0 = y1;
orientations.clear();
}
break;
default:
m_pObject = NULL;
return false;
}
}
if (!bDone)
{
m_dAngle = M_PI / 2;
points->coords[0] = m_xn[0] = x1 = m_x0;
points->coords[1] = m_xn[1] = y1 = m_y0;
}
for (i = 2; i < m_size * 2; i+= 2)
{
m_xn[i] = x1 += m_dLength * cos(m_dAngle - m_dDev * (i / 2 - 1));
m_xn[i + 1] = y1 -= m_dLength * sin(m_dAngle - m_dDev * (i / 2 - 1));
points->coords[i] = x1;
points->coords[i + 1] = y1;
}
m_bAllowed = CheckIfAllowed();
m_pItem = gnome_canvas_item_new(
m_pGroup,
gnome_canvas_polygon_get_type(),
"points", points,
"outline_color", (m_bAllowed)? AddColor: DeleteColor,
"width_units", pTheme->GetBondWidth (),
NULL);
return true;
}
void gcpCycleTool::OnDrag()
{
if (!m_size) return;
int i;
double x1, y1, x2, y2;
bool bDone = false;
GnomeCanvasItem* pItem = gnome_canvas_get_item_at(GNOME_CANVAS(m_pWidget), m_x, m_y);
gcpDocument *pDoc = m_pView->GetDoc ();
gcpTheme *pTheme = pDoc->GetTheme ();
if (pItem == (GnomeCanvasItem*)m_pBackground) pItem = NULL;
Object* pObject = NULL;
if (pItem) pObject = (Object*)g_object_get_data(G_OBJECT(pItem), "object");
if (m_pObject)
{
if (m_pObject->GetType() == BondType)
{
if (((gcpBond*)m_pObject)->GetDist(m_x / m_dZoomFactor, m_y / m_dZoomFactor) < (pTheme->GetPadding () + pTheme->GetBondWidth () / 2) * m_dZoomFactor)
{
if (m_pItem)
{
gtk_object_destroy(GTK_OBJECT(GNOME_CANVAS_ITEM(m_pItem)));
m_pItem = NULL;
}
return;
}
x1 = (m_x - m_xn[0]) * (m_xn[3] - m_xn[1]) + (m_xn[1] - m_y) * (m_xn[2] - m_xn[0]);
if (m_nState & GDK_SHIFT_MASK)
{
if (m_Chain->GetLength() == (unsigned) m_size - 2) return;
gcpBond* pBond;
if (pObject)
{
if (pObject->GetType() == AtomType)
{
if (m_Chain->Contains((gcpAtom*)pObject))return;
pBond = (gcpBond*)m_Start->GetBond((gcpAtom*)pObject);
if (pBond)
{
m_Chain->AddBond((gcpAtom*)pObject, m_Start);
m_Start = (gcpAtom*)pObject;
}
else
{
pBond = (gcpBond*)m_End->GetBond((gcpAtom*)pObject);
if (pBond)
{
m_Chain->AddBond(m_End, (gcpAtom*)pObject);
m_End = (gcpAtom*)pObject;
}
}
if (pBond) bDone = true;
}
else if (pObject->GetType() == BondType)
{
m_pAtom = (gcpAtom*)((gcpBond*)pObject)->GetAtom(m_Start);
if (m_pAtom)
{
if (m_Chain->Contains(m_pAtom))return;
m_Chain->AddBond(m_pAtom, m_Start);
m_Start = m_pAtom;
}
else
{
m_pAtom = (gcpAtom*)((gcpBond*)pObject)->GetAtom(m_End);
if (m_pAtom)
{
if (m_Chain->Contains(m_pAtom))return;
m_Chain->AddBond(m_End, m_pAtom);
m_End = m_pAtom;
}
}
if (m_pAtom) bDone = true;
}
if (!bDone) return;
m_Start->GetCoords(&x1, &y1);
x1 *= m_dZoomFactor;
y1 *= m_dZoomFactor;
m_End->GetCoords(&x2, &y2);
x2 *= m_dZoomFactor;
y2 *= m_dZoomFactor;
double d = sqrt(square(x2 - x1) + square(y2 - y1)), L = pDoc->GetBondLength () * m_dZoomFactor, t;
unsigned n = m_size - m_Chain->GetLength();
if (d < L * n)
{//FIXME: this algorithm is far from optimal!!!
double t1, k = d/L, p = M_PI /n;
t = 0;
while(1)
{
t1 = t + (p - t) * (1 - k / ((t != 0.0)? (sin(t * n) / sin(t)): n));
if (fabs(t1 - t) < 1e-15) break; //An infinite loop might be possible here!
t = t1;
}
}
else t = 0;
if (t < fabs(m_dDev) / 4) //too large, change the bond length
{
t = fabs(m_dDev) / 4;
}
//Find center
double dx, dy;
if (m_dDev > 0)
{
dx = ((y1 - y2) / tan(M_PI - t * n) + x2 + x1) / 2;
dy = ((x1 - x2) / tan(t * n) + y1 + y2) / 2;
t = - t;
}
else
{
dx = ((y2 - y1) / tan(M_PI - t * n) + x2 + x1) / 2;
dy = ((x2 - x1) / tan(t * n) + y1 + y2) / 2;
}
double a0 = atan2(dy - y2, x2 - dx);
d = sqrt(square(dy - y2) + square(dx - x2));
i = 0;
m_pAtom = m_Start;
do
{
m_pAtom->GetCoords(&x1, &y1);
m_xn[i] = points->coords[i] = x1 * m_dZoomFactor;
i++;
m_xn[i] = points->coords[i] = y1 * m_dZoomFactor;
i++;
}
while ((m_pAtom != m_End) && (m_pAtom = m_Chain->GetNextAtom(m_pAtom)));
while (i < m_size * 2)
{
a0 += 2 * t;
m_xn[i] = points->coords[i] = dx + d * cos(a0);
i++;
m_xn[i] = points->coords[i] = dy - d * sin(a0);
i++;
}
}
bDone = true;
}
else if (x1 * m_dDev > 0)
{
m_dDev = - m_dDev;
x1 = m_xn[2];
y1 = m_xn[3];
for (i = 4; i < m_size * 2; i+= 2)
{
m_xn[i] = x1 += (pDoc->GetBondLength () * m_dZoomFactor) * cos (m_dAngle - m_dDev * (i / 2 - 1));
m_xn[i + 1] = y1 -= (pDoc->GetBondLength () * m_dZoomFactor) * sin (m_dAngle - m_dDev * (i / 2 - 1));
points->coords[i] = x1;
points->coords[i + 1] = y1;
}
bDone = true;
}
else if (m_pItem) return;
else bDone = true;
}
}
if (m_pItem)
{
gtk_object_destroy(GTK_OBJECT(GNOME_CANVAS_ITEM(m_pItem)));
m_pItem = NULL;
}
if (!bDone)
{
double dAngle;
m_pAtom = NULL;
if (pObject)
{
if (pObject->GetType() == BondType)
{
m_pAtom = (gcpAtom*)pObject->GetAtomAt(m_x / m_dZoomFactor, m_y / m_dZoomFactor);
}
else if (pObject->GetType() == AtomType)
{
m_pAtom = (gcpAtom*)pObject;
}
}
if (m_pAtom)
{
if (m_pAtom == m_pObject) return;
m_pAtom->GetCoords(&m_x, &m_y);
m_x *= m_dZoomFactor;
m_y *= m_dZoomFactor;
m_x -= m_x0;
m_y -= m_y0;
double BondLength = sqrt(square(m_x) + square(m_y));
if (m_x == 0)
{
if (m_y == 0) return;
dAngle = (m_y < 0) ? 90 : 270;
}
else
{
m_dAngle = atan(-m_y/m_x);
if (m_x < 0) m_dAngle += M_PI;
}
x1 = m_x0;
y1 = m_y0;
for (i = 2; i < m_size * 2; i+= 2)
{
m_xn[i] = x1 += (BondLength) * cos(m_dAngle - m_dDev * (i / 2 - 1));
m_xn[i + 1] = y1 -= (BondLength) * sin(m_dAngle - m_dDev * (i / 2 - 1));
points->coords[i] = x1;
points->coords[i + 1] = y1;
}
}
else
{
m_x -= m_x0;
m_y -= m_y0;
if (m_x == 0)
{
if (m_y == 0) return;
dAngle = (m_y < 0) ? 90 : 270;
}
else
{
dAngle = atan(-m_y/m_x) * 180 / M_PI;
if (!(m_nState & GDK_CONTROL_MASK)) dAngle = rint(dAngle / 5) * 5;
if (m_x < 0) dAngle += 180;
}
m_dAngle = dAngle * M_PI / 180;
x1 = m_x0;
y1 = m_y0;
double d = (m_nState & GDK_SHIFT_MASK)? sqrt (square (m_x) + square (m_y)): pDoc->GetBondLength () * m_dZoomFactor;
for (i = 2; i < m_size * 2; i+= 2)
{
m_xn[i] = x1 += d * cos(m_dAngle - m_dDev * (i / 2 - 1));
m_xn[i + 1] = y1 -= d * sin(m_dAngle - m_dDev * (i / 2 - 1));
points->coords[i] = x1;
points->coords[i + 1] = y1;
}
char tmp[32];
if (dAngle < 0) dAngle += 360;
snprintf(tmp, sizeof(tmp) - 1, _("Orientation: %g"), dAngle);
m_pApp->SetStatusText(tmp);
}
}
m_bAllowed = CheckIfAllowed();
m_pItem = gnome_canvas_item_new(
m_pGroup,
gnome_canvas_polygon_get_type(),
"points", points,
"outline_color", (m_bAllowed)? AddColor: DeleteColor,
"width_units", pTheme->GetBondWidth (),
NULL);
}
void gcpCycleTool::OnRelease()
{
if (!m_size)
return;
if (m_Chain) {
delete m_Chain;
m_Chain = NULL;
}
if (m_pItem) {
gtk_object_destroy (GTK_OBJECT (GNOME_CANVAS_ITEM (m_pItem)));
m_pItem = NULL;
}
else
return;
if (!m_bAllowed)
return;
m_pApp->ClearStatus ();
gcpAtom* pAtom[m_size];
gcpBond* pBond;
Object *pObject;
char const *Id;
GnomeCanvasItem* pItem;
gcpDocument *pDoc = m_pView->GetDoc ();
gcpOperation *pOp = NULL;
gcpMolecule *pMol = NULL;
char const *MolId = NULL;
int i;
for (i = 0; i < m_size; i++)
{
m_x = m_xn[2 * i];
m_y = m_xn[2 * i + 1];
pAtom[i] = NULL;
pItem = gnome_canvas_get_item_at(GNOME_CANVAS(m_pWidget), m_x, m_y);
if (pItem == (GnomeCanvasItem*)m_pBackground) pItem = NULL;
m_pObject = (pItem)? (Object*)g_object_get_data(G_OBJECT(pItem), "object"): NULL;
if (MergeAtoms && m_pObject)
{
if (m_pObject->GetType() == BondType)
{
pAtom[i] = (gcpAtom*)m_pObject->GetAtomAt(m_x / m_dZoomFactor, m_y / m_dZoomFactor);
}
else if (m_pObject->GetType() == AtomType)
{
pAtom[i] = (gcpAtom*)m_pObject;
}
if (pAtom[i]) {
if (pMol == NULL) {
pMol = reinterpret_cast<gcpMolecule *> (pAtom[i]->GetMolecule ());
pMol->Lock (true);
// we must store the id, the molecule might be destroyed
// FIXME! what happens to the parent in such a case?
MolId = pMol->GetId ();
}
// if group not in ModifiedObjects, add it and save it
pObject = pAtom[i]->GetGroup ();
Id = pObject->GetId ();
if (ModifiedObjects.find (Id) == ModifiedObjects.end ()) {
if (!pOp)
pOp = pDoc->GetNewOperation (GCP_MODIFY_OPERATION);
pOp->AddObject (pObject);
ModifiedObjects.insert (Id);
}
}
}
if (!pAtom[i])
{
pAtom[i] = new gcpAtom(m_pApp->GetCurZ(), m_xn[2 * i] / m_dZoomFactor, m_xn[2 * i + 1] / m_dZoomFactor, 0);
pDoc->AddAtom(pAtom[i]);
}
if (i)
{
pBond = (gcpBond*)pAtom[i]->GetBond(pAtom[i - 1]);
if (!pBond)
{
pBond = new gcpBond(pAtom[i - 1], pAtom[i], 1);
pDoc->AddBond(pBond);
}
}
}
pBond = (gcpBond*)pAtom[m_size - 1]->GetBond(pAtom[0]);
if (!pBond)
{
pBond = new gcpBond(pAtom[m_size - 1], pAtom[0], 1);
pDoc->AddBond(pBond);
}
pObject = pBond->GetGroup ();
if (pOp) {
ModifiedObjects.insert (pObject->GetId ());
set<string>::iterator it, end = ModifiedObjects.end();
for (it = ModifiedObjects.begin(); it != end; it++) {
pObject = pDoc->GetDescendant ((*it).c_str ());
if (pObject)
pOp->AddObject (pObject, 1);
}
} else {
pOp = pDoc->GetNewOperation (GCP_ADD_OPERATION);
pOp->AddObject (pObject);
}
pDoc->FinishOperation ();
if (pMol)
pMol = static_cast <gcpMolecule*> (pDoc->GetDescendant (MolId));
if (pMol) {
pMol->Lock (false);
pMol->EmitSignal (OnChangedSignal);
}
ModifiedObjects.clear ();
}
void gcpCycleTool::OnChangeState()
{
if (m_pObject && (m_pObject->GetType() == BondType))
{
if (m_nState & GDK_SHIFT_MASK)
{
if (!m_Chain)
{
if (m_Direct)
{
m_Start = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(0);
m_End = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(1);
}
else
{
m_Start = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(1);
m_End = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(0);
}
m_Chain = new gcpChain((gcpBond*)m_pObject, m_Start);
}
}
else
{
double x1, y1, x2, y2;
if (m_pItem)
{
gtk_object_destroy(GTK_OBJECT(GNOME_CANVAS_ITEM(m_pItem)));
m_pItem = NULL;
}
if (m_Direct)
{
m_Start = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(0);
m_End = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(1);
}
else
{
m_Start = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(1);
m_End = (gcpAtom*)((gcpBond*)m_pObject)->GetAtom(0);
}
m_Start->GetCoords(&x1, &y1);
m_End->GetCoords(&x2, &y2);
points->coords[0] = m_xn[0] = x1 * m_dZoomFactor;
points->coords[1] = m_xn[1] = y1 * m_dZoomFactor;
points->coords[2] = m_xn[2] = x1 = x2 * m_dZoomFactor;
points->coords[3] = m_xn[3] = y1 = y2 * m_dZoomFactor;
for (int i = 4; i < m_size * 2; i+= 2)
{
gcpDocument *pDoc = m_pView->GetDoc ();
m_xn[i] = x1 += (pDoc->GetBondLength () * m_dZoomFactor) * cos(m_dAngle - m_dDev * (i / 2 - 1));
m_xn[i + 1] = y1 -= (pDoc->GetBondLength () * m_dZoomFactor) * sin(m_dAngle - m_dDev * (i / 2 - 1));
points->coords[i] = x1;
points->coords[i + 1] = y1;
}
if (m_Chain)
{
delete m_Chain;
m_Chain = NULL;
}
}
}
m_bAllowed = CheckIfAllowed();
gcpTool::OnChangeState();
}
void gcpCycleTool::Init()
{
m_xn = new double[m_size * 2];
m_dDev = 2 * M_PI / m_size;
points = gnome_canvas_points_new(m_size);
}
bool gcpCycleTool::CheckIfAllowed()
{
//Search atoms at the positions of the vertices and check if adding bonds to them is allowed
gcpAtom* pAtom[m_size];
GnomeCanvasItem* pItem;
Object* pObject;
int i, n;
for (i = 0; i < m_size; i++)
{
m_x = m_xn[2 * i];
m_y = m_xn[2 * i + 1];
pItem = gnome_canvas_get_item_at(GNOME_CANVAS(m_pWidget), m_x, m_y);
if (pItem == (GnomeCanvasItem*)m_pBackground) pItem = NULL;
pObject = (pItem)? (Object*)g_object_get_data(G_OBJECT(pItem), "object"): NULL;
if (MergeAtoms && pObject)
{
TypeId Id = pObject->GetType();
switch (Id)
{
case BondType:
case FragmentType:
pAtom[i] = (gcpAtom*)pObject->GetAtomAt(m_x / m_dZoomFactor, m_y / m_dZoomFactor);
break;
case AtomType:
pAtom[i] = (gcpAtom*)pObject;
break;
default:
pAtom[i] = NULL;
}
}
else pAtom[i] = NULL;
}
for (i = 0; i < m_size; i++)
{
if (!pAtom[i]) continue;
n = 0;
if (!pAtom[i]->GetBond(pAtom[(i)? i - 1: m_size -1])) n++;
if (!pAtom[i]->GetBond(pAtom[(i < m_size - 1)? i + 1: 0])) n++;
if (n && (!pAtom[i]->AcceptNewBonds(n))) return false;
}
return true;
}
static void on_length_changed (GtkSpinButton *btn, gcpCycleTool *tool)
{
tool->SetLength (gtk_spin_button_get_value (btn));
}
static void on_merge_toggled (GtkToggleButton *btn)
{
MergeAtoms = gtk_toggle_button_get_active (btn);
}
void gcpCycleTool::SetLength (double length)
{
m_pApp->GetActiveDocument ()->SetBondLength (length);
}
GtkWidget *gcpCycleTool::GetPropertyPage ()
{
GladeXML *xml = glade_xml_new (GLADEDIR"/cycle.glade", "cycle", GETTEXT_PACKAGE);
m_LengthBtn = GTK_SPIN_BUTTON (glade_xml_get_widget (xml, "bond-length"));
g_signal_connect (m_LengthBtn, "value-changed", G_CALLBACK (on_length_changed), this);
m_MergeBtn = GTK_TOGGLE_BUTTON (glade_xml_get_widget (xml, "merge"));
g_signal_connect (m_MergeBtn, "toggled", G_CALLBACK (on_merge_toggled), NULL);
return glade_xml_get_widget (xml, "cycle");
}
void gcpCycleTool::Activate ()
{
gcpDocument *pDoc = m_pApp->GetActiveDocument ();
gtk_spin_button_set_value (m_LengthBtn, pDoc->GetBondLength ());
gtk_toggle_button_set_active (m_MergeBtn, MergeAtoms);
}
gcpNCycleTool::gcpNCycleTool(gcpApplication* App, unsigned char size): gcpCycleTool(App, 9)
{
SetSize(size);
}
gcpNCycleTool::~gcpNCycleTool()
{
}
void gcpNCycleTool::SetSize(unsigned char size)
{
if (m_size)
{
delete [] m_xn;
gnome_canvas_points_free(points);
}
if ((m_size = size)) Init();
}
static void on_size_changed (GtkSpinButton *button, gcpNCycleTool *tool)
{
tool->SetSize ((unsigned char) gtk_spin_button_get_value_as_int (button));
}
GtkWidget *gcpNCycleTool::GetPropertyPage ()
{
GladeXML *xml = glade_xml_new (GLADEDIR"/cyclen.glade", "cycle", GETTEXT_PACKAGE);
m_LengthBtn = GTK_SPIN_BUTTON (glade_xml_get_widget (xml, "bond-length"));
g_signal_connect (m_LengthBtn, "value-changed", G_CALLBACK (on_length_changed), this);
m_MergeBtn = GTK_TOGGLE_BUTTON (glade_xml_get_widget (xml, "merge"));
g_signal_connect (m_MergeBtn, "toggled", G_CALLBACK (on_merge_toggled), NULL);
m_SizeBtn = GTK_SPIN_BUTTON (glade_xml_get_widget (xml, "sizebtn"));
gtk_spin_button_set_value (m_SizeBtn, m_size);
g_signal_connect (m_SizeBtn, "value-changed", G_CALLBACK (on_size_changed), this);
return glade_xml_get_widget (xml, "cycle");
}
syntax highlighted by Code2HTML, v. 0.9.1