// -*- C++ -*- /* * GChemPaint library * reaction-arrow.cc * * Copyright (C) 2004-2007 Jean Bréfort * * 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 "reaction-arrow.h" #include "reaction.h" #include "reaction-step.h" #include "document.h" #include "settings.h" #include "theme.h" #include "view.h" #include "widgetdata.h" #include "libgcpcanvas/gcp-canvas-group.h" #include "libgcpcanvas/gcp-canvas-line.h" #include #include gcpReactionArrow::gcpReactionArrow (gcpReaction* react, unsigned Type): gcpArrow (ReactionArrowType) { SetId ((char*) "ra1"); m_Type = Type; m_Start = m_End = NULL; if (react) react->AddChild (this); m_TypeChanged = false; } gcpReactionArrow::~gcpReactionArrow() { if (IsLocked ()) return; if (m_Start) m_Start->RemoveArrow (this); if (m_End) m_End->RemoveArrow (this); } xmlNodePtr gcpReactionArrow::Save (xmlDocPtr xml) { xmlNodePtr parent, node; node = xmlNewDocNode (xml, NULL, (xmlChar*) "reaction-arrow", NULL); if (!node) return NULL; if (!gcpArrow::Save (xml, node)) { xmlFreeNode (node); return NULL; } xmlNewProp (node, (xmlChar*) "type", (xmlChar*) ((m_Type == gcpSimpleArrow)? "single": "double")); if (m_Type == gcpFullReversibleArrow) xmlNewProp (node, (xmlChar*) "heads", (xmlChar*) "full"); if (m_Start) xmlNewProp (node, (xmlChar*) "start", (xmlChar*) m_Start->GetId ()); if (m_End) xmlNewProp (node, (xmlChar*) "end", (xmlChar*) m_End->GetId ()); gcpReaction* r = (gcpReaction*) GetReaction(); if (!r) { //save the arrow as an object (this is NOT safe) parent = xmlNewDocNode (xml, NULL, (xmlChar*) "object", NULL); if (node && parent) xmlAddChild (parent, node); else { xmlFreeNode(node); return NULL; } } else parent = node; return parent; } bool gcpReactionArrow::Load (xmlNodePtr node) { char *buf; Object *parent; if (gcpArrow::Load (node)) { buf = (char*) xmlGetProp (node, (xmlChar*) "type"); if (buf) { if (!strcmp (buf, "double")) { m_Type = gcpReversibleArrow; char *buf0 = (char*) xmlGetProp (node, (xmlChar*) "heads"); if (buf0) { if (!strcmp (buf0, "full")) m_Type = gcpFullReversibleArrow; xmlFree (buf0); } m_TypeChanged = true; } xmlFree (buf); } parent = GetParent (); if (!parent) return true; buf = (char*) xmlGetProp (node, (xmlChar*) "start"); if (buf) { m_Start = reinterpret_cast (parent->GetDescendant (buf)); xmlFree (buf); if (!m_Start) return false; m_Start->AddArrow (this); } buf = (char*) xmlGetProp (node, (xmlChar*) "end"); if (buf) { m_End = reinterpret_cast (parent->GetDescendant (buf)); xmlFree (buf); if (!m_End) return false; m_End->AddArrow (this); } return true; } return false; } void gcpReactionArrow::Add (GtkWidget* w) { gcpWidgetData* pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (w), "data"); gcpTheme *pTheme = pData->View->GetDoc ()->GetTheme (); GnomeCanvasPoints *points = gnome_canvas_points_new (2); GnomeCanvasGroup* group = GNOME_CANVAS_GROUP(gnome_canvas_item_new (pData->Group, gnome_canvas_group_ext_get_type (), NULL)); GnomeCanvasItem* item; switch(m_Type) { case gcpSimpleArrow: points->coords[0] = m_x * pTheme->GetZoomFactor (); points->coords[1] = m_y * pTheme->GetZoomFactor (); points->coords[2] = (m_x + m_width) * pTheme->GetZoomFactor (); points->coords[3] = (m_y + m_height) * pTheme->GetZoomFactor (); item = gnome_canvas_item_new ( group, gnome_canvas_line_ext_get_type (), "points", points, "fill_color", (pData->IsSelected (this))? SelectColor: Color, "width_units", pTheme->GetArrowWidth (), "last_arrowhead", true, "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), "last_arrowhead_style", (unsigned char) ARROW_HEAD_BOTH, NULL); g_object_set_data (G_OBJECT (item), "object", this); g_object_set_data (G_OBJECT (group), "arrow", item); g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w); break; case gcpReversibleArrow: { double dAngle = atan (- m_height / m_width); if (m_width < 0) dAngle += M_PI; points->coords[0] = m_x * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[1] = m_y * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * cos (dAngle); points->coords[2] = (m_x + m_width) * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[3] = (m_y + m_height) * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * cos (dAngle); item = gnome_canvas_item_new ( group, gnome_canvas_line_ext_get_type (), "points", points, "fill_color", (pData->IsSelected(this))? SelectColor: Color, "width_units", pTheme->GetArrowWidth (), "last_arrowhead", true, "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), "last_arrowhead_style", (unsigned char) ARROW_HEAD_LEFT, NULL); g_object_set_data (G_OBJECT (item), "object", this); g_object_set_data (G_OBJECT (group), "direct", item); g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w); points->coords[2] = m_x * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[3] = m_y * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * cos (dAngle); points->coords[0] = (m_x + m_width) * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * sin( dAngle); points->coords[1] = (m_y + m_height) * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * cos (dAngle); item = gnome_canvas_item_new ( group, gnome_canvas_line_ext_get_type (), "points", points, "fill_color", (pData->IsSelected (this))? SelectColor: Color, "width_units", pTheme->GetArrowWidth (), "last_arrowhead", true, "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), "last_arrowhead_style", (unsigned char) ARROW_HEAD_LEFT, NULL); g_object_set_data (G_OBJECT (item), "object", this); g_object_set_data (G_OBJECT (group), "reverse", item); g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w); break; } case gcpFullReversibleArrow: { double dAngle = atan (- m_height / m_width); if (m_width < 0) dAngle += M_PI; points->coords[0] = m_x * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[1] = m_y * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * cos (dAngle); points->coords[2] = (m_x + m_width) * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[3] = (m_y + m_height) * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * cos (dAngle); item = gnome_canvas_item_new( group, gnome_canvas_line_ext_get_type (), "points", points, "fill_color", (pData->IsSelected (this))? SelectColor: Color, "width_units", pTheme->GetArrowWidth (), "last_arrowhead", true, "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), "last_arrowhead_style", (unsigned char) ARROW_HEAD_BOTH, NULL); g_object_set_data (G_OBJECT (item), "object", this); g_object_set_data (G_OBJECT (group), "direct", item); g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w); points->coords[2] = m_x * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[3] = m_y * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * cos (dAngle); points->coords[0] = (m_x + m_width) * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[1] = (m_y + m_height) * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * cos (dAngle); item = gnome_canvas_item_new ( group, gnome_canvas_line_ext_get_type (), "points", points, "fill_color", (pData->IsSelected (this))? SelectColor: Color, "width_units", pTheme->GetArrowWidth (), "last_arrowhead", true, "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), "last_arrowhead_style", (unsigned char) ARROW_HEAD_BOTH, NULL); g_object_set_data (G_OBJECT (item), "object", this); g_object_set_data (G_OBJECT (group), "reverse", item); g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w); break; } } pData->Items[this] = group; gnome_canvas_points_free (points); } void gcpReactionArrow::Update (GtkWidget* w) { gcpWidgetData* pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (w), "data"); if (pData->Items[this] != NULL) return; gcpTheme *pTheme = pData->View->GetDoc ()->GetTheme (); GnomeCanvasGroup* group = pData->Items[this]; if (!group) { Add (w); m_TypeChanged = false; return; } if (m_TypeChanged) { gtk_object_destroy (GTK_OBJECT (group)); Add (w); m_TypeChanged = false; return; } GnomeCanvasPoints *points = gnome_canvas_points_new (2); switch(m_Type) { case gcpSimpleArrow: points->coords[0] = m_x * pTheme->GetZoomFactor (); points->coords[1] = m_y * pTheme->GetZoomFactor (); points->coords[2] = (m_x + m_width) * pTheme->GetZoomFactor (); points->coords[3] = (m_y + m_height) * pTheme->GetZoomFactor (); g_object_set (G_OBJECT (g_object_get_data (G_OBJECT (group), "arrow")), "points", points, "width_units", pTheme->GetArrowWidth (), "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), NULL); break; case gcpReversibleArrow: case gcpFullReversibleArrow: double dAngle = atan (- m_height / m_width); if (m_width < 0) dAngle += M_PI; points->coords[0] = m_x * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[1] = m_y * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * cos (dAngle); points->coords[2] = (m_x + m_width) * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[3] = (m_y + m_height) * pTheme->GetZoomFactor () - pTheme->GetArrowDist () / 2 * cos (dAngle); g_object_set (G_OBJECT (g_object_get_data (G_OBJECT (group), "direct")), "points", points, "width_units", pTheme->GetArrowWidth (), "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), NULL); points->coords[2] = m_x * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[3] = m_y * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * cos (dAngle); points->coords[0] = (m_x + m_width) * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * sin (dAngle); points->coords[1] = (m_y + m_height) * pTheme->GetZoomFactor () + pTheme->GetArrowDist () / 2 * cos (dAngle); g_object_set (G_OBJECT (g_object_get_data (G_OBJECT (group), "reverse")), "points", points, "width_units", pTheme->GetArrowWidth (), "arrow_shape_a", pTheme->GetArrowHeadA (), "arrow_shape_b", pTheme->GetArrowHeadB (), "arrow_shape_c", pTheme->GetArrowHeadC (), NULL); break; } gnome_canvas_points_free (points); } void gcpReactionArrow::RemoveStep (gcpReactionStep *Step) { if (Step == m_Start) m_Start = NULL; else if (Step == m_End) m_End = NULL; }