// -*- C++ -*-
/*
* GChemPaint atoms plugin
* chargetool.cc
*
* Copyright (C) 2003-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 "chargetool.h"
#include "lib/settings.h"
#include "lib/document.h"
#include "lib/application.h"
#include "lib/theme.h"
#include "libgcpcanvas/gcp-canvas-group.h"
#include "libgcpcanvas/gcp-canvas-pango.h"
#include <glib/gi18n-lib.h>
#include <cmath>
gcpChargeTool::gcpChargeTool(gcpApplication *App, string Id): gcpTool(App, Id)
{
if (Id == string ("ChargePlus"))
m_glyph = (char*) "\xE2\x8a\x95";
else if (Id == string ("ChargeMinus"))
m_glyph = (char*) "\xE2\x8a\x96";
else
m_glyph = 0;
}
gcpChargeTool::~gcpChargeTool()
{
}
bool gcpChargeTool::OnClicked()
{
if (!m_pObject || (m_pObject->GetType () != AtomType))
return false;
gcpAtom *pAtom = (gcpAtom*) m_pObject;
gcpTheme *Theme = m_pView->GetDoc ()->GetTheme ();
m_Charge = pAtom->GetCharge () + ((GetName() == string("ChargePlus"))? 1: -1);
if (!pAtom->AcceptCharge (m_Charge))
return false;
m_bDragged = false;
GObject *obj;
ArtDRect rect;
pAtom->GetCoords (&m_x0, &m_y0);
m_x0 *= m_dZoomFactor;
m_y0 *= m_dZoomFactor;
if (m_pObject->GetParent ()->GetType () == FragmentType) {
obj = G_OBJECT (m_pData->Items[m_pObject->GetParent ()]);
gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (g_object_get_data (obj, "fragment")),
&rect.x0, &rect.y0, &rect.x1, &rect.y0);
} else {
obj = G_OBJECT (m_pData->Items[m_pObject]);
GnomeCanvasItem *sym = (GnomeCanvasItem*) g_object_get_data (obj, "symbol");
if (sym)
gnome_canvas_item_get_bounds (sym,
&rect.x0, &rect.y0, &rect.x1, &rect.y0);
else
rect.y0 = m_y0 + 6;
}
GnomeCanvasItem *item = (GnomeCanvasItem*) g_object_get_data (obj, "charge");
m_dDist = 0;
m_pData->GetObjectBounds (m_pObject, &rect);
m_dDistMax = 1.5 * fabs (rect.y0 - m_y0);
if (m_Charge) {
if (item)
gnome_canvas_item_hide (item);
double x, y, xc = 0., yc;
m_DefaultPos = 0xff;
int align = ((gcpAtom*)m_pObject)->GetChargePosition(m_DefaultPos, 0., x, y);
if (!align) return false;
m_Pos = m_DefaultPos;
x *= m_dZoomFactor;
y *= m_dZoomFactor;
if (!m_Pos) {
m_x = x - m_x0;
m_y = y - m_y0;
m_dAngle = atan (- m_y / m_x);
if (m_x < 0)
m_dAngle += M_PI;
m_dDist = sqrt (m_x * m_x + m_y * m_y);
} else {
switch (m_Pos) {
case CHARGE_NE:
m_dAngle = M_PI / 4;
break;
case CHARGE_NW:
m_dAngle = 3 * M_PI / 4;
break;
case CHARGE_N:
m_dAngle = M_PI / 2;
break;
case CHARGE_SE:
m_dAngle = 7 * M_PI / 4;
break;
case CHARGE_SW:
m_dAngle = 5 * M_PI / 4;
break;
case CHARGE_S:
m_dAngle = 3 * M_PI / 2;
break;
case CHARGE_E:
m_dAngle = 0.;
break;
case CHARGE_W:
m_dAngle = M_PI;
break;
}
}
char* markup = NULL;
PangoLayout* pl = NULL;
if (abs (m_Charge) > 1) {
markup = g_strdup_printf ("%d", abs (m_Charge));
PangoContext* pc = m_pView->GetPangoContext();
PangoRectangle rect;
pl = pango_layout_new (pc);
pango_layout_set_text (pl, markup, -1);
pango_layout_get_extents (pl, NULL, &rect);
m_ChargeWidth = rect.width / PANGO_SCALE;
pango_layout_set_font_description (pl, m_pView->GetPangoSmallFontDesc ());
m_ChargeTWidth = m_ChargeWidth + 1. + Theme->GetChargeSignSize ();
} else {
m_ChargeWidth = 0.;
m_ChargeTWidth = Theme->GetChargeSignSize ();
}
switch (align) {
case -2:
xc = x + m_ChargeTWidth / 2. - Theme->GetChargeSignSize ();
y += Theme->GetChargeSignSize () / 2.;
break;
case -1:
xc = x - Theme->GetChargeSignSize () - Theme->GetPadding ();
break;
case 0:
case -3:
xc = x + m_ChargeTWidth / 2. - Theme->GetChargeSignSize ();
break;
case 1:
xc = x + m_ChargeWidth + Theme->GetPadding ();
break;
case 2:
xc = x + m_ChargeTWidth / 2. - Theme->GetChargeSignSize ();
y -= Theme->GetChargeSignSize () / 2.;
break;
}
x = xc - 1.;
yc = y - Theme->GetChargeSignSize () / 2.;
m_x1 = x;
m_y1 = y;
m_pItem = gnome_canvas_item_new (
m_pGroup,
gnome_canvas_group_get_type(),
NULL);
if (markup) {
gnome_canvas_item_new(
GNOME_CANVAS_GROUP (m_pItem),
gnome_canvas_pango_get_type(),
"fill_color", AddColor,
"layout", pl,
"anchor", GTK_ANCHOR_EAST,
"x", x,
"y", y,
NULL);
}
gnome_canvas_item_new (
GNOME_CANVAS_GROUP (m_pItem),
gnome_canvas_ellipse_get_type (),
"x1", xc,
"y1", yc,
"x2", xc + Theme->GetChargeSignSize (),
"y2", yc + Theme->GetChargeSignSize (),
"outline_color", AddColor,
"width_units", 0.5,
NULL
);
ArtBpath *path = art_new (ArtBpath, 5);
path[0].code = ART_MOVETO_OPEN;
path[0].x3 = xc + 1.;
path[1].code = ART_LINETO;
path[1].x3 = xc + Theme->GetChargeSignSize () - 1.;
path[0].y3 = path[1].y3 = yc + Theme->GetChargeSignSize () / 2.;
if (m_Charge > 0) {
path[2].code = ART_MOVETO_OPEN;
path[2].y3 = yc + 1.;
path[3].code = ART_LINETO;
path[3].y3 = yc + Theme->GetChargeSignSize () - 1.;
path[2].x3 = path[3].x3 = xc + Theme->GetChargeSignSize () / 2.;
path[4].code = ART_END;
} else
path[2].code = ART_END;
GnomeCanvasPathDef *cpd = gnome_canvas_path_def_new_from_bpath (path);
item = gnome_canvas_item_new (
GNOME_CANVAS_GROUP (m_pItem),
gnome_canvas_bpath_get_type (),
"bpath", cpd,
"outline_color", AddColor,
"width_units", .75,
NULL
);
gnome_canvas_path_def_unref (cpd);
if (pl)
g_object_unref (G_OBJECT (pl));
} else {
void *child = g_object_get_data (obj, "figure");
if (child)
g_object_set (G_OBJECT (child), "fill-color", DeleteColor, NULL);
child = g_object_get_data (obj, "circle");
g_object_set (G_OBJECT (child), "outline-color", DeleteColor, NULL);
child = g_object_get_data (obj, "sign");
g_object_set (G_OBJECT (child), "outline-color", DeleteColor, NULL);
}
char buf[32];
snprintf (buf, sizeof (buf) - 1, _("Orientation: %g"), m_dAngle * 180. / M_PI);
m_pApp->SetStatusText (buf);
m_bChanged = true;
return true;
}
void gcpChargeTool::OnDrag()
{
if (m_Charge && !m_pItem)
return;
m_bDragged = true;
GObject *obj = G_OBJECT ((m_pObject->GetParent ()->GetType () == FragmentType)?
m_pData->Items[m_pObject->GetParent ()]: m_pData->Items[m_pObject]);
GnomeCanvasItem *item = (GnomeCanvasItem*) g_object_get_data (obj, "charge");
int align, old_pos = m_Pos;
m_x -= m_x0;
m_y -= m_y0;
m_dDist = sqrt (square (m_x) + square (m_y));
if (!m_pItem) {
void *child;
if (m_dDist < m_dDistMax) {
if (!m_bChanged) {
child = g_object_get_data (obj, "figure");
if (child)
g_object_set (G_OBJECT (child), "fill-color", DeleteColor, NULL);
child = g_object_get_data (obj, "circle");
g_object_set (G_OBJECT (child), "outline-color", DeleteColor, NULL);
child = g_object_get_data (obj, "sign");
g_object_set (G_OBJECT (child), "outline-color", DeleteColor, NULL);
m_bChanged = true;
}
} else {
if (m_bChanged) {
child = g_object_get_data (obj, "figure");
if (child)
g_object_set (G_OBJECT (child), "fill-color", "black", NULL);
child = g_object_get_data (obj, "circle");
g_object_set (G_OBJECT (child), "outline-color", "black", NULL);
child = g_object_get_data (obj, "sign");
g_object_set (G_OBJECT (child), "outline-color", "black", NULL);
m_bChanged = false;
}
}
return;
}
double Angle = atan (- m_y / m_x);
if (isnan (Angle))
Angle = m_dAngle;
else if (m_x < 0)
Angle += M_PI;
if (!(m_nState & GDK_CONTROL_MASK)) {
int pos = (int) rint (Angle * 4. / M_PI);
Angle = (double) pos * M_PI / 4.;
if (m_nState & GDK_SHIFT_MASK)
pos = 8;
else if (pos < 0)
pos += 8;
switch (pos) {
case 0:
m_Pos = POSITION_E;
break;
case 1:
m_Pos = POSITION_NE;
break;
case 2:
m_Pos = POSITION_N;
break;
case 3:
m_Pos = POSITION_NW;
break;
case 4:
m_Pos = POSITION_W;
break;
case 5:
m_Pos = POSITION_SW;
break;
case 6:
m_Pos = POSITION_S;
break;
case 7:
m_Pos = POSITION_SE;
break;
default:
m_Pos = 0;
}
} else
m_Pos = 0;
if ((Angle == m_dAngle) && !(m_nState & GDK_SHIFT_MASK)) {
if (m_dDist < m_dDistMax) {
if (!m_bChanged) {
gnome_canvas_item_show (m_pItem);
if (item)
gnome_canvas_item_hide (item);
m_bChanged = true;
}
} else {
if (m_bChanged) {
if (item)
gnome_canvas_item_show (item);
gnome_canvas_item_hide (m_pItem);
m_bChanged = false;
}
}
} else {
double x, y;
gcpAtom *pAtom = (gcpAtom*)m_pObject;
gcpTheme *Theme = m_pView->GetDoc ()->GetTheme ();
if (!(m_nState & GDK_SHIFT_MASK) && (m_dDist >= m_dDistMax) && m_bChanged) {
gnome_canvas_item_hide (m_pItem);
m_bChanged = false;
} else if ((align = pAtom->GetChargePosition (m_Pos, Angle * 180. / M_PI, x, y))) {
m_dAngle = Angle;
if (m_nState & GDK_SHIFT_MASK) {
align = 0;
x = m_x0 + m_dDist * cos (m_dAngle);
y = m_y0 - m_dDist * sin (m_dAngle);
} else {
x = x * m_dZoomFactor;
y = y * m_dZoomFactor;
}
switch (align) {
case -2:
x += m_ChargeTWidth / 2. - Theme->GetChargeSignSize () - 1.;
y += Theme->GetChargeSignSize () / 2.;
break;
case -1:
x-= Theme->GetChargeSignSize () + Theme->GetPadding ();
break;
case -3:
x += m_ChargeTWidth / 2. - Theme->GetChargeSignSize () - 1.;
break;
case 1:
x += m_ChargeWidth + Theme->GetPadding ();
break;
case 2:
x += m_ChargeTWidth / 2. - Theme->GetChargeSignSize () - 1.;
y -= Theme->GetChargeSignSize () / 2.;
break;
}
gnome_canvas_item_move (m_pItem, x - m_x1, y - m_y1);
m_x1 = x;
m_y1 = y;
gnome_canvas_item_show (m_pItem);
if (item)
gnome_canvas_item_hide (item);
m_bChanged = true;
} else
m_Pos = old_pos;
}
char tmp[32];
snprintf(tmp, sizeof(tmp) - 1, _("Orientation: %g"), m_dAngle * 180. / M_PI);
m_pApp->SetStatusText(tmp);
}
void gcpChargeTool::OnRelease()
{
if (m_bChanged)
{
gcpAtom* pAtom = (gcpAtom*) m_pObject;
gcpDocument* pDoc = m_pView->GetDoc();
gcpOperation* pOp = pDoc-> GetNewOperation(GCP_MODIFY_OPERATION);
GObject *obj = G_OBJECT ((m_pObject->GetParent ()->GetType () == FragmentType)?
m_pData->Items[m_pObject->GetParent ()]: m_pData->Items[m_pObject]);
GnomeCanvasItem *item = (GnomeCanvasItem*) g_object_get_data (obj, "charge");
if (item)
gnome_canvas_item_show (item);
m_pObject = m_pObject->GetGroup ();
pOp->AddObject(m_pObject, 0);
pAtom->SetCharge(m_Charge);
if (!m_bDragged) {
double x, y;
m_DefaultPos = 0xff;
pAtom->GetChargePosition(m_DefaultPos, 0., x, y);
if (m_Pos && (m_Pos != m_DefaultPos))
m_Pos = m_DefaultPos;
}
if (!(m_nState & GDK_SHIFT_MASK))
m_dDist = 0.;
pAtom->SetChargePosition (m_Pos, m_Pos == m_DefaultPos,
m_dAngle, m_dDist / m_dZoomFactor);
pAtom->Update();
m_pView->Update(m_pObject);
pAtom->EmitSignal (OnChangedSignal);
pOp->AddObject(m_pObject, 1);
pDoc->FinishOperation();
}
}
syntax highlighted by Code2HTML, v. 0.9.1