// -*- C++ -*-
/*
* GChemPaint library
* reactant.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 "reactant.h"
#include "reaction-step.h"
#include "application.h"
#include "document.h"
#include "text.h"
#include "theme.h"
#include <glib/gi18n-lib.h>
#include <cstring>
extern xmlDocPtr pXmlDoc;
gcpReactant::gcpReactant (): Object (ReactantType)
{
SetId ((char*) "r1");
m_Stoich = 0;
Child = NULL;
Stoichiometry = NULL;
}
gcpReactant::gcpReactant (gcpReactionStep* step, Object *object) throw (invalid_argument): Object (ReactantType)
{
SetId ((char*) "r1");
step->AddChild (this);
GetDocument ()->EmptyTranslationTable();
static const set<TypeId>& allowed_types = Object::GetRules ("reactant", RuleMayContain);
if (allowed_types.find (object->GetType ()) == allowed_types.end ())
throw invalid_argument ("invalid reactant");
AddChild (object);
Child = object;
Stoichiometry = NULL;
m_Stoich = 0;
}
gcpReactant::~gcpReactant ()
{
}
static void do_add_stoichiometry (gcpReactant *reactant)
{
reactant->AddStoichiometry ();
}
bool gcpReactant::BuildContextualMenu (GtkUIManager *UIManager, Object *object, double x, double y)
{
bool result = false;
if (m_Stoich == 0 && !Stoichiometry) {
GtkActionGroup *group = gtk_action_group_new ("reactant");
GtkAction *action = gtk_action_new ("stoichiometry", _("Add a stoichiometry coefficient"), 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='stoichiometry'/></popup></ui>";
gtk_ui_manager_add_ui_from_string (UIManager, buf, -1, NULL);
GtkWidget *w = gtk_ui_manager_get_widget (UIManager, "/popup/stoichiometry");
g_signal_connect_swapped (w, "activate", G_CALLBACK (do_add_stoichiometry), this);
result = true;
}
return result | GetParent ()->BuildContextualMenu (UIManager, object, x, y);
}
xmlNodePtr gcpReactant::Save (xmlDocPtr xml)
{
if (!Child)
return NULL;
xmlNodePtr node = xmlNewDocNode (xml, NULL, (const xmlChar*) "reactant", NULL);
SaveId (node);
xmlNodePtr child = Child->Save (xml);
xmlAddChild (node, child);
if (Stoichiometry) {
xmlNodePtr stoich = Stoichiometry->Save (xml);
xmlNodeSetName (stoich, (const xmlChar*) "stoichiometry");
xmlAddChild (node, stoich);
}
return node;
}
bool gcpReactant::Load (xmlNodePtr node)
{
xmlChar* buf;
xmlNodePtr child;
Lock ();
buf = xmlGetProp (node, (xmlChar*) "id");
if (buf) {
SetId ((char*) buf);
xmlFree (buf);
}
child = node->children;
gcpDocument *pDoc = (gcpDocument*) GetDocument ();
while (child) {
if (!strcmp ((const char*) child->name, "stoichiometry")) {
if (Stoichiometry) {
Lock (false);
return false;
}
Stoichiometry = new gcpText ();
AddChild (Stoichiometry);
if (!Stoichiometry->Load (child)) {
delete Stoichiometry;
Lock (false);
return false;
};
pDoc->AddObject (Stoichiometry);
} else {
if (Child) {
if (strcmp ((const char*) child->name, "text")) {
Lock (false);
return false;
} else {
child = child->next;
continue;
}
}
Child = CreateObject ((const char*) child->name, this);
if (Child) {
AddChild (Child);
if (!Child->Load (child)) {
delete Child;
Child = NULL;
}
}
}
child = child->next;
}
Lock (false);
return (Child != NULL);
}
double gcpReactant::GetYAlign ()
{
return (Child)? Child->GetYAlign (): 0.;
}
void gcpReactant::AddStoichiometry ()
{
gcpDocument *pDoc = dynamic_cast <gcpDocument*> (GetDocument ());
gcpApplication * pApp = pDoc->GetApplication ();
gcpView *pView = pDoc->GetView ();
gcpTheme *pTheme= pDoc->GetTheme ();
gcpWidgetData *pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data");
ArtDRect rect;
pData->GetObjectBounds (this, &rect);
double x = rect.x0 / pTheme->GetZoomFactor ();
gcpText *text = new gcpText (x, GetYAlign () + pView->GetBaseLineOffset ());
Stoichiometry = text;
AddChild (text);
pDoc->AddObject (text);
gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
pData->GetObjectBounds (text, &rect);
Child->Move (rect.x1 / pTheme->GetZoomFactor () + pTheme->GetStoichiometryPadding () - x, 0.);
gcpTool *tool = pApp->GetTool ("Text");
GetParent ()->EmitSignal (OnChangedSignal);
pApp->ActivateTool ("Text", true);
tool->OnClicked (pView, text, rect.x0 * pTheme->GetZoomFactor (), GetYAlign () * pTheme->GetZoomFactor (), 0);
}
bool gcpReactant::OnSignal (SignalId Signal, Object *Obj)
{
if (Signal == OnChangedSignal) {
gcpDocument *pDoc = (gcpDocument*) GetDocument ();
gcpTheme *pTheme= pDoc->GetTheme ();
gcpWidgetData *pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data");
ArtDRect rect;
unsigned n = GetChildrenNumber ();
map<string, Object*>::iterator i;
Object *pObj;
if (n == 0)
delete this;
else if (n == 1) {
if (Stoichiometry) {
// Child or stoichiometry have been deleted
pObj = GetFirstChild (i);
if (pObj == Child)
Stoichiometry = NULL;
else {
pDoc->Remove (Stoichiometry);
delete this;
}
}
} else if ((n == 2) && Stoichiometry) {
// Just need to space the two children
gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
pData->GetObjectBounds (Stoichiometry, &rect);
double x = rect.x1 / pTheme->GetZoomFactor () + pTheme->GetStoichiometryPadding ();
pData->GetObjectBounds (Child, &rect);
Child->Move (x - rect.x0 / pTheme->GetZoomFactor (), 0.);
PangoLayout *layout = ((gcpTextObject*) Stoichiometry)->GetLayout ();
const char *txt = pango_layout_get_text (layout);
char *endptr;
int n = strtol (txt, &endptr, 10);
m_Stoich = (!*endptr)? n: 0;
} else {
// Most probably child has been splitted
xmlNodePtr node = NULL;
bool ChildFound = false;
gcpReactionStep *step = reinterpret_cast<gcpReactionStep*> (GetParent ());
if (Stoichiometry)
node = Stoichiometry->Save (pXmlDoc);
pObj = GetFirstChild (i);
while (pObj) {
if (pObj == Child)
ChildFound = true;
else if (pObj != Stoichiometry) {
gcpReactant *reactant = new gcpReactant (step, pObj);
if (Stoichiometry) {
reactant->Stoichiometry = new gcpText ();
reactant->AddChild (reactant->Stoichiometry);
pDoc->AddObject (reactant->Stoichiometry);
reactant->Stoichiometry->Load (node);
reactant->EmitSignal (OnChangedSignal);
}
pObj = GetFirstChild (i);
continue;
}
pObj = GetNextChild (i);
}
if (!ChildFound) {
if (Stoichiometry)
pDoc->Remove (Stoichiometry);
delete this;
}
if (node)
xmlFreeNode (node);
EmitSignal (OnChangedSignal);
}
}
return true;
}
syntax highlighted by Code2HTML, v. 0.9.1