// -*- C++ -*-
/*
* GChemPaint library
* mesomery.cc
*
* Copyright (C) 2002-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 "document.h"
#include "theme.h"
#include "widgetdata.h"
#include "view.h"
#include "mesomery.h"
#include "mesomery-arrow.h"
#include "mesomer.h"
#include <glib/gi18n-lib.h>
#include <cmath>
#include <set>
#include <cstring>
gcpMesomery::gcpMesomery (): Object (MesomeryType)
{
SetId ((char*) "msy1");
}
gcpMesomery::~gcpMesomery ()
{
if (IsLocked ())
return;
map<string, Object*>::iterator i;
Object *pObj;
gcpMesomeryArrow *arrow;
gcpDocument *pDoc = reinterpret_cast<gcpDocument *> (GetDocument ());
gcpOperation *pOp = pDoc->GetCurrentOperation ();
while ((pObj = GetFirstChild (i))) {
if (pObj->GetType () == MesomeryArrowType) {
arrow = reinterpret_cast<gcpMesomeryArrow*> (pObj);
arrow->SetStartMesomer (NULL);
arrow->SetEndMesomer (NULL);
arrow->SetParent (GetParent ());
if (pOp)
pOp->AddObject (arrow, 1);
} else
delete pObj;
}
}
bool gcpMesomery::Load (xmlNodePtr node)
{
xmlChar* tmp;
xmlNodePtr child;
Object* pObject;
list<xmlNodePtr> arrows;
Lock ();
tmp = xmlGetProp (node, (xmlChar*) "id");
if (tmp) {
SetId ((char*) tmp);
xmlFree (tmp);
}
child = node->children;
while (child) {
if (!strcmp ((const char*) child->name, "mesomery-arrow"))
arrows.push_front (child);
else {
pObject = CreateObject ((const char*) child->name, this);
if (pObject) {
if (!pObject->Load (child))
delete pObject;
} else {
Lock (false);
return false;
}
}
child = child->next;
}
while (!arrows.empty ()) {
child = arrows.back ();
pObject = CreateObject ("mesomery-arrow", this);
if (pObject) {
if (!pObject->Load (child))
delete pObject;
} else {
Lock (false);
return false;
}
arrows.pop_back ();
}
Lock (false);
return true;
}
typedef struct
{
double x, y;
ArtDRect r;
gcpMesomer *mes;
} ObjectData;
bool gcpMesomery::Build (list<Object*>& Children) throw (invalid_argument)
{
gcpDocument *pDoc = dynamic_cast<gcpDocument *> (GetDocument ());
gcpTheme *pTheme = pDoc->GetTheme ();
gcpWidgetData *pData= reinterpret_cast<gcpWidgetData *> (g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data"));
list<Object *>::iterator i, iend = Children.end ();
map<Object *, ObjectData> Objects;
list<Object *> Arrows;
double minright, minleft, x, y, x0, y0, x1, y1, l, d, ps;
Object *Left, *Right;
ObjectData od;
gcpMesomeryArrow *arrow;
unsigned narrows = 0, nmol = 0;
for (i = Children.begin (); i != iend; i++) {
pData->GetObjectBounds (*i, &od.r);
od.x = (od.r.x0 + od.r.x1) / 2.;
od.y = (*i)->GetYAlign () * pTheme->GetZoomFactor ();
switch ((*i)->GetType ()) {
case MoleculeType:
od.mes = new gcpMesomer (this, dynamic_cast<gcpMolecule *>(*i));
Objects[*i] = od;
nmol++;
break;
case MesomeryArrowType:
narrows++;
Arrows.push_back (*i);
AddChild (*i);
break;
default:
throw invalid_argument (_("Something wrong happened, please file a bug report."));
}
}
// now, for each arrow, search closiest object on both sides and verify it's a molecule
list<Object *>::iterator j, jend = Arrows.end ();
map<Object *, ObjectData>::iterator k, kend = Objects.end ();
for (j = Arrows.begin (); j != jend; j++) {
arrow = reinterpret_cast<gcpMesomeryArrow *>(*j);
arrow->GetCoords (&x0, &y0, &x1, &y1);
//x0 and y0 should be the center of the arrow, not the beginning, so we must transform them
x0 = (x0 + x1) / 2;
y0 = (y0 + y1) / 2;
// x1, y1 will now be the coordinates of a normalized vector:
x1 -= x0;
y1 -= y0;
x0 *= pTheme->GetZoomFactor ();
y0 *= pTheme->GetZoomFactor ();
l = sqrt (x1 * x1 + y1 * y1);
x1 /= l;
y1 /= l;
l *= pTheme->GetZoomFactor (); // half length of the arrow on the screen
// No molecule should be nearer than that
minright = minleft = DBL_MAX;
Left = Right = NULL;
for (k = Objects.begin (); k != kend; k++) {
od = (*k).second;
x = od.x - x0;
y = od.y - y0;
d = sqrt (x * x + y * y);
ps = (x * x1 + y * y1) / d;
if (ps >= -.71 && ps <= .71)
continue;
if (d < l) {
Left = (*k).first;
Right = *j;
pData->UnselectAll ();
pData->SetSelected (Left);
pData->SetSelected (Right);
throw invalid_argument (_("No space left between molecule and arrow!"));
}
if (ps < 0) {
if (d < minleft) {
Left = od.mes;
minleft = d;
}
} else {
if (d < minright) {
Right = od.mes;
minright = d;
}
}
}
if (!Left || !Right) { // Do not accept arrows with only one mesomer (?)
Left = *j;
pData->UnselectAll ();
pData->SetSelected (Left);
throw invalid_argument (_("Isolated arrows are not allowed!"));
}
reinterpret_cast<gcpMesomeryArrow *> (*j)->SetStartMesomer (reinterpret_cast<gcpMesomer *> (Left));
reinterpret_cast<gcpMesomeryArrow *> (*j)->SetEndMesomer (reinterpret_cast<gcpMesomer *> (Right));
reinterpret_cast<gcpMesomer *> (Left)->AddArrow (reinterpret_cast<gcpMesomeryArrow *> (*j), reinterpret_cast<gcpMesomer *> (Right));
reinterpret_cast<gcpMesomer *> (Right)->AddArrow (reinterpret_cast<gcpMesomeryArrow *> (*j), reinterpret_cast<gcpMesomer *> (Left));
}
// now, check if each mesomer has at least one arrow, may be we should add missing arrows?
for (k = Objects.begin (); k != kend; k++) {
od = (*k).second;
if (!od.mes->Validate ()) {
Left = (*k).first;
pData->UnselectAll ();
pData->SetSelected (Left);
throw invalid_argument (_("Isolated molecule!\n Please add missing arrows."));
}
}
// Check if all mesomers are related (only connectivity is checked for now)
if (!Validate (false))
throw invalid_argument (_("Please add missing arrows."));
// Align the children
Align ();
return true;
}
static void BuildConnectivity ( set<Object *> &Objects, gcpMesomer* Mesomer)
{
map<gcpMesomer *, gcpMesomeryArrow *> *Arrows = Mesomer->GetArrows ();
map<gcpMesomer *, gcpMesomeryArrow *>::iterator i, end = Arrows->end ();
for (i = Arrows->begin (); i != end; i++) {
Objects.insert ((*i).second);
if (Objects.find ((*i).first) == Objects.end ()) {
Objects.insert ((*i).first);
BuildConnectivity (Objects, (*i).first);
}
}
}
bool gcpMesomery::Validate (bool split)
{
map<string, Object*>::iterator i;
Object *pObj = GetFirstChild (i);
while (pObj && pObj->GetType () != MesomerType)
pObj = GetNextChild (i);
if (pObj == NULL)
return false;
set<Object *> Objects;
Objects.insert (pObj);
BuildConnectivity (Objects, reinterpret_cast<gcpMesomer *> (pObj));
while (Objects.size () < GetChildrenNumber ()) {
if (!split)
return 2;
pObj = GetFirstChild (i);
while (pObj && (pObj->GetType () != MesomerType))
pObj = GetNextChild (i);
if (reinterpret_cast<gcpMesomer *> (pObj)->Validate ()) {
gcpMesomery *ms = new gcpMesomery (GetParent (),
reinterpret_cast<gcpMesomer *> (pObj));
gcpDocument *pDoc = reinterpret_cast<gcpDocument*> (GetDocument ());
gcpOperation *pOp = pDoc->GetCurrentOperation ();
pOp->AddObject (ms, 1);
} else
delete pObj;
}
return true;
}
typedef struct MC
{
list<gcpMesomer*> mesomers;
struct MC *prev;
list<struct MC*> children;
} MesomeryChain;
typedef struct
{
double x, y, dx, dy;
ArtDRect r;
gcpMesomer *m;
MesomeryChain *chain;
} MesomerData;
static void TerminateChain (MesomeryChain *chain, set<Object*> &core,
set<MesomeryChain*> &terminated_chains, map<Object*, MesomerData> &positions)
{
if (terminated_chains.find (chain) != terminated_chains.end ())
return; // job has already been done
list<gcpMesomer*>::iterator m, mend;
MesomerData md;
if (chain->prev != NULL)
TerminateChain (chain->prev, core, terminated_chains, positions);
mend = chain->mesomers.end ();
for (m = chain->mesomers.begin (), m++; m != mend; m++) {
md = positions[*m];
md.chain = NULL;
positions[*m] = md;
core.insert (*m);
}
terminated_chains.insert (chain);
list<struct MC*>::iterator i, iend = chain->children.end ();
for (i = chain->children.begin (); i != iend; i++)
(*i)->prev = NULL;
}
static void DoAlign (gcpMesomeryArrow *arrow, MesomerData &start, MesomerData &end, double ArrowPadding, double ZoomFactor)
{
double x0, y0, x1, y1, x, y, l, dx, dy;
bool horiz;
arrow->GetCoords (&x0, &y0, &x1, &y1);
x = x1 - x0;
y = y1 - y0;
l = sqrt (x * x + y * y);
x /= l;
y /= l;
if ((fabs (x) > 1e-5) && (fabs (y) > 1e-5))
horiz = (fabs (x) > fabs (y));
else if (fabs (x) > 1e-5)
horiz = true;
else
horiz = false;
if (horiz) {
if (x > 0)
dx = start.r.x1 - start.x + ArrowPadding;
else
dx = start.r.x0 - start.x - ArrowPadding;
dy = dx * y / x;
} else {
if (y > 0)
dy = start.r.y1 - start.y + ArrowPadding;
else
dy = start.r.y0 - start.y - ArrowPadding;
dx = dy * x / y;
}
x1 += x0 = (dx + start.x) / ZoomFactor - x0;
y1 += y0 = (dy + start.y) / ZoomFactor - y0;
arrow->Move (x0, y0);
if (horiz) {
if (x > 0)
dx = end.x - end.r.x0 + ArrowPadding;
else
dx = end.x - end.r.x1 - ArrowPadding;
dy = dx * y / x;
} else {
if (y > 0)
dy = end.y - end.r.y0 + ArrowPadding;
else
dy = end.y - end.r.y1 - ArrowPadding;
dx = dy * x / y;
}
dx = x1 * ZoomFactor - (end.x - dx);
dy = y1 * ZoomFactor - (end.y - dy);
end.r.x0 += dx;
end.r.x1 += dx;
end.x += dx;
end.dx += dx;
end.r.y0 += dy;
end.r.y1 += dy;
end.y += dy;
end.dy += dy;
}
static double GetProjectionRatio (MesomerData &md, MesomerData &md0, MesomerData &md1, gcpMesomeryArrow *arrow)
{
double x0, y0, x1, y1, d;
arrow->GetCoords (&x0, &y0, &x1, &y1);
x1 -= x0;
y1 -= y0;
x0 = md1.x - md0.x;
y0 = md1.y - md0.y;
d = (x0 * y1 - x1 * y0);
if (d == 0.)
return -1.; // everything is aligned, and this should not occur, but who knows?
return ((md.x - md0.x) * y1 - (md.y - md0.y) * x1)/ d;
}
static void AlignArrow (gcpMesomeryArrow *arrow, MesomerData &md0, MesomerData &md1, double ArrowPadding, double ZoomFactor)
{
double dx, dy, x, y, x0, y0, x1, y1, l;
bool horiz;
if (arrow->GetStartMesomer () != md0.m)
arrow->Reverse ();
x = md1.x - md0.x;
y = md1.y - md0.y;
l = sqrt (x * x + y * y);
x /= l;
y /= l;
if ((fabs (x) > 1e-5) && (fabs (y) > 1e-5))
horiz = (fabs (x) > fabs (y));
else if (fabs (x) > 1e-5)
horiz = true;
else
horiz = false;
if (horiz) {
if (x > 0)
dx = md0.r.x1 - md0.x + ArrowPadding;
else
dx = md0.r.x0 - md0.x - ArrowPadding;
dy = dx * y / x;
} else {
if (y > 0)
dy = md0.r.y1 - md0.y + ArrowPadding;
else
dy = md0.r.y0 - md0.y - ArrowPadding;
dx = dy * x / y;
}
x0 = (dx + md0.x) / ZoomFactor;
y0 = (dy + md0.y) / ZoomFactor;
if (horiz) {
if (x > 0)
dx = md1.x - md1.r.x0 + ArrowPadding;
else
dx = md1.x - md1.r.x1 - ArrowPadding;
dy = dx * y / x;
} else {
if (y > 0)
dy = md1.y - md1.r.y0 + ArrowPadding;
else
dy = md1.y - md1.r.y1 - ArrowPadding;
dx = dy * x / y;
}
x1 = (md1.x - dx) / ZoomFactor;
y1 = (md1.y - dy) / ZoomFactor;
arrow->SetCoords (x0, y0, x1, y1);
}
static void ChainMove (MesomeryChain *chain, double dx, double dy, map<Object*, MesomerData> &positions)
{
list<gcpMesomer*>::iterator i, iend;
list<struct MC*>::iterator c, cend;
MesomerData md;
iend = chain->mesomers.end ();
for (i = chain->mesomers.begin (), i++; i!= iend; i++) {
md = positions [*i];
md.r.x0 += dx;
md.r.x1 += dx;
md.x += dx;
md.dx += dx;
md.r.y0 += dy;
md.r.y1 += dy;
md.y += dy;
md.dy += dy;
positions[*i] = md;
}
cend = chain->children.end ();
for (c = chain->children.begin (); c != cend; c++)
ChainMove (*c, dx, dy, positions);
}
static void ChainAdjust (MesomerData &md0, MesomerData &md1, double ratio, map<Object*, MesomerData> &positions)
{
MesomeryChain *chain = md0.chain, *parent;
MesomerData md;
double x = 0., y = 0.;
ratio -= 1.;
list<gcpMesomer*>::reverse_iterator m, mend;
list<struct MC*>::iterator c, cend;
while (chain) {
for (m = chain->mesomers.rbegin (); m != mend; m++) {
if (*m == md1.m)
return;
md = positions[*m];
x = (md.x - md1.x) * ratio;
y = (md.y - md1.y) * ratio;
md.r.x0 += x;
md.r.x1 += x;
md.x += x;
md.dx += x;
md.r.y0 += y;
md.r.y1 += y;
md.y += y;
md.dy += y;
positions[md.m] = md;
}
parent = chain->prev;
if (parent == NULL)
return; // This should not occur, may be we should fire an exception there.
cend = parent->children.end ();
for (c = parent->children.begin (); c != cend; c++) {
if (*c == chain)
continue;
ChainMove (*c, x, y, positions);
}
parent = chain;
}
}
// The following value is quite arbitrary, it is there to avoid getting very large images
#define CUTOFF 4.0
void gcpMesomery::Align ()
{
gcpDocument *pDoc = dynamic_cast <gcpDocument *> (GetDocument ());
gcpView *pView = pDoc->GetView ();
gcpTheme *pTheme = pDoc->GetTheme ();
gcpWidgetData *pData = reinterpret_cast <gcpWidgetData *> (g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data"));
/* Update the canvas if necessary */
GnomeCanvas* w = GNOME_CANVAS (((gcpDocument*) GetDocument ())->GetWidget ());
while (w->idle_id)
gtk_main_iteration();
gnome_canvas_update_now (w);
map<string, Object*>::iterator i;
Object *pObj = GetFirstChild (i);
gcpMesomer *pStart = NULL, *mes0, *mes;
map<Object*, MesomerData> positions;
MesomerData md, md0, md1, md2;
double d;
md.dx = md.dy = 0.;
MesomeryChain *ch;
while (pObj) {
if (pObj->GetType () == MesomerType) {
pData->GetObjectBounds (pObj, &md.r);
md.x = (md.r.x0 + md.r.x1) / 2.;
md.y = pObj->GetYAlign () * pTheme->GetZoomFactor ();
md.chain = NULL;
md.m = dynamic_cast<gcpMesomer*> (pObj);
positions[pObj] = md;
if (!pStart)
pStart = md.m;
}
pObj = GetNextChild (i);
}
if (pStart == NULL)
throw invalid_argument (_("Something wrong happened, please file a bug report."));
set<Object*> Core;
Core.insert (pStart);
// initialize chains starting from pStart
list<MesomeryChain*> chains;
set<MesomeryChain*> terminated_chains;
list<MesomeryChain*>::iterator c, cend;
list<gcpMesomer*>::iterator m, mend;
list<gcpMesomer*>::reverse_iterator mr;
MesomeryChain *chain;
map<gcpMesomer *, gcpMesomeryArrow *> *arrows = pStart->GetArrows ();
map<gcpMesomer *, gcpMesomeryArrow *>::iterator j, jend = arrows->end ();
md0 = positions[pStart];
for (j = arrows->begin (); j != jend; j++) {
// At this point we don't need to check anything, just create the chains
chain = new MesomeryChain;
chain->prev = NULL;
chain->mesomers.push_front (pStart);
chain->mesomers.push_back ((*j).first);
md = positions[(*j).first];
md.chain = chain;
chains.push_back (chain);
// align the molecule
if ((*j).second->GetStartMesomer () != pStart)
(*j).second->Reverse ();
DoAlign ((*j).second, md0, md, pTheme->GetArrowPadding (), pTheme->GetZoomFactor ());
positions[(*j).first] = md;
}
/* now add one mesomer to each growing chain and terminate chains when the end is
reached or when a cycle is found */
while (!chains.empty ()) {
cend = chains.end ();
for (c = chains.begin (); c != cend; c++) {
// here we need the end of the chain and the previous mesomer
mr = (*c)->mesomers.rbegin ();
mes0 = *mr;
mr++;
mes = *mr;
arrows = mes0->GetArrows ();
switch (arrows->size ()) {
case 1:
// this is the end of the chain
TerminateChain (*c, Core, terminated_chains, positions);
break;
case 2:
arrows = mes0->GetArrows ();
j = arrows->begin ();
if ((*j).first == mes)
j++;
if ((*j).second->GetStartMesomer () != mes0)
(*j).second->Reverse ();
md0 = positions[mes0];
md = positions[(*j).first];
if (Core.find ((*j).first) != Core.end ()) {
// we reach a mesomer already in the core: cycle
if (Core.find (mes0) == Core.end ()) {
// find start atom for the chain
ch = *c;
while (ch->prev)
ch = ch->prev;
md1 = positions[ch->mesomers.front ()];
d = GetProjectionRatio (md, md1, md0, (*j).second);
if (d > 0.999999 && d < CUTOFF)
ChainAdjust (md0, md1, d, positions);
}
TerminateChain (*c, Core, terminated_chains, positions);
} else if (md.chain != NULL) {
// we have just found a new cycle
// find start atom for both chains
ch = *c;
while (ch->prev)
ch = ch->prev;
md1 = positions[ch->mesomers.front ()];
ch = md.chain;
while (ch->prev)
ch = ch->prev;
md2 = positions[ch->mesomers.front ()];
// Check if mes0 are in core or not (this might happen)
if (Core.find (mes0) != Core.end ()) {
/* we are already in core, try to align the other chain
and change the arrow if not possible */
d = GetProjectionRatio (md0, md2, md, (*j).second);
if (d > 0.999999)
ChainAdjust (md, md2, d, positions);
} else {
d = GetProjectionRatio (md, md1, md0, (*j).second);
if (d > 0.999999 && d < CUTOFF)
ChainAdjust (md0, md1, d, positions);
else {
d = GetProjectionRatio (md0, md2, md, (*j).second);
if (d > 0.999999 && d < CUTOFF)
ChainAdjust (md, md2, d, positions);
}
}
TerminateChain (md.chain, Core, terminated_chains, positions);
TerminateChain (*c, Core, terminated_chains, positions);
} else {
md.chain = md0.chain;
md.chain->mesomers.push_back ((*j).first);
DoAlign ((*j).second, md0, md, pTheme->GetArrowPadding (), pTheme->GetZoomFactor ());
positions[(*j).first] = md;
}
break;
default:
arrows = mes0->GetArrows ();
jend = arrows->end ();
for (j = arrows->begin (); j != jend; j++) {
if ((*j).first == mes)
continue;
if ((*j).second->GetStartMesomer () != mes0)
(*j).second->Reverse ();
md0 = positions[mes0];
md = positions[(*j).first];
if (Core.find ((*j).first) != Core.end ()) {
// we reach a mesomer already in the core: cycle
if (Core.find (mes0) == Core.end ()) {
// find start atom for the chain
ch = *c;
while (ch->prev)
ch = ch->prev;
md1 = positions[ch->mesomers.front ()];
d = GetProjectionRatio (md, md1, md0, (*j).second);
if (d > 0.999999 && d < CUTOFF)
ChainAdjust (md0, md1, d, positions);
}
TerminateChain (*c, Core, terminated_chains, positions);
} else if (md.chain != NULL) {
// we have just found a new cycle
// we have just found a new cycle
// find start atom for both chains
ch = *c;
while (ch->prev)
ch = ch->prev;
md1 = positions[ch->mesomers.front ()];
ch = md.chain;
while (ch->prev)
ch = ch->prev;
md2 = positions[ch->mesomers.front ()];
// Check if mes0 are in core or not (this might happen)
if (Core.find (mes0) != Core.end ()) {
/* we are already in core, try to align the other chain
and change the arrow if not possible */
d = GetProjectionRatio (md0, md2, md, (*j).second);
if (d > 0.999999 && d < CUTOFF)
ChainAdjust (md, md2, d, positions);
} else {
d = GetProjectionRatio (md, md1, md0, (*j).second);
if (d > 0.999999 && d < CUTOFF)
ChainAdjust (md0, md1, d, positions);
else {
d = GetProjectionRatio (md0, md2, md, (*j).second);
if (d > 0.999999)
ChainAdjust (md, md2, d, positions);
}
}
TerminateChain (md.chain, Core, terminated_chains, positions);
TerminateChain (*c, Core, terminated_chains, positions);
} else {
md.chain = md0.chain;
md.chain->mesomers.push_back ((*j).first);
DoAlign ((*j).second, md0, md, pTheme->GetArrowPadding (), pTheme->GetZoomFactor ());
positions[(*j).first] = md;
}
}
break;
}
}
set<MesomeryChain*>::iterator d, dend = terminated_chains.end ();
for (d = terminated_chains.begin (); d != dend; d++) {
chains.remove (*d);
delete *d;
}
terminated_chains.clear ();
}
// really move mesomers
map<Object*, MesomerData>::iterator p, pend= positions.end ();
for (p = positions.begin (); p != pend; p++)
(*p).first->Move ((*p).second.dx / pTheme->GetZoomFactor (), (*p).second.dy / pTheme->GetZoomFactor ());
gcpMesomeryArrow *arrow;
for (pObj = GetFirstChild (i); pObj; pObj = GetNextChild (i))
if (pObj->GetType () == MesomeryArrowType) {
arrow = static_cast<gcpMesomeryArrow*> (pObj);
md = positions[arrow->GetStartMesomer ()];
md0 = positions[arrow->GetEndMesomer ()];
AlignArrow (arrow, md, md0, pTheme->GetArrowPadding (), pTheme->GetZoomFactor ());
}
pView->Update (this);
}
/* Transform2D is just here to inhibit rotation of retrosynthesis while we do not have
real 2D alignment (only vertical at the moment */
void gcpMesomery::Transform2D (Matrix2D& m, double x, double y)
{
}
bool gcpMesomery::OnSignal (SignalId Signal, Object *Child)
{
Validate (true);
if (GetChildrenNumber () < 3)
delete this;
else
Align ();
return true;
}
static void do_destroy_mesomery (void *data)
{
gcpMesomery *ms = reinterpret_cast<gcpMesomery *> (data);
gcpDocument *pDoc = reinterpret_cast<gcpDocument *> (ms->GetDocument ());
gcpWidgetData *pData = reinterpret_cast<gcpWidgetData *> (g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data"));
pData->Unselect (ms);
gcpOperation *pOp = pDoc->GetNewOperation (GCP_MODIFY_OPERATION);
pOp->AddObject (ms, 0);
delete ms;
pDoc->FinishOperation ();
}
bool gcpMesomery::BuildContextualMenu (GtkUIManager *UIManager, Object *object, double x, double y)
{
GtkActionGroup *group = gtk_action_group_new ("mesomery");
GtkAction *action = gtk_action_new ("destroy-ms", _("Destroy the mesomery relationship"), NULL, NULL);
gtk_action_group_add_action (group, action);
g_object_unref (action);
gtk_ui_manager_insert_action_group (UIManager, group, 0);
g_object_unref (group);
char buf[] = "<ui><popup><menuitem action='destroy-ms'/></popup></ui>";
gtk_ui_manager_add_ui_from_string (UIManager, buf, -1, NULL);
GtkWidget *w = gtk_ui_manager_get_widget (UIManager, "/popup/destroy-ms");
g_signal_connect_swapped (w, "activate", G_CALLBACK (do_destroy_mesomery), this);
GetParent ()->BuildContextualMenu (UIManager, object, x, y);
return true;
}
gcpMesomery::gcpMesomery (Object* parent, gcpMesomer *mesomer): Object (MesomeryType)
{
SetId ((char*) "msy1");
SetParent (parent);
AddChild (mesomer);
set<Object *> Objects;
BuildConnectivity (Objects, mesomer);
set<Object *>::iterator i, end = Objects.end ();
for (i = Objects.begin (); i != end; i++)
AddChild (*i);
Align ();
}
double gcpMesomery::GetYAlign ()
{
map<string, Object*>::iterator i;
Object *pObj;
pObj = GetFirstChild (i);
double y = DBL_MAX, new_y;
while (pObj) {
if (pObj->GetType () == MesomerType)
if ((new_y = pObj->GetYAlign ()) < y)
y = new_y;
pObj = GetNextChild (i);
}
return y;
}
syntax highlighted by Code2HTML, v. 0.9.1