// -*- C++ -*-

/* 
 * GChemPaint text plugin
 * fragmenttool.cc 
 *
 * Copyright (C) 2003-2006 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 "fragmenttool.h"
#include "lib/fragment.h"
#include "lib/document.h"
#include "lib/application.h"
#include "lib/settings.h"
#include "lib/theme.h"
#include "lib/window.h"
#include <gdk/gdkkeysyms.h>

extern xmlDocPtr pXmlDoc;

using namespace gcu;

gcpFragmentTool::gcpFragmentTool (gcpApplication *App): gcpTextTool (App, "Fragment")
{
}

gcpFragmentTool::~gcpFragmentTool ()
{
	if (ClipboardData)
		xmlFree (ClipboardData);
}

static void on_get_data (GtkClipboard *clipboard, GtkSelectionData *selection_data,  guint info, gcpFragmentTool* tool)
{
	tool->OnGetData (clipboard, selection_data, info);
}

bool gcpFragmentTool::OnClicked ()
{
	if (m_Active && ((m_pObject == NULL) || (m_pObject->GetType() != FragmentType) ||
			(m_Active != g_object_get_data (G_OBJECT (m_pData->Items[m_pObject]), "fragment")))) {
		if (!Unselect ())
			return false;
	}
	gcpDocument* pDoc = m_pView->GetDoc ();
	if (!m_pObject) {
		gcpTheme *pTheme = pDoc->GetTheme ();
		gcpFragment *fragment = new gcpFragment (m_x0 / pTheme->GetZoomFactor (), m_y0 / pTheme->GetZoomFactor ());
		pDoc->AddFragment (fragment);
		pDoc->AbortOperation ();
		pDoc->EmptyTranslationTable ();
		m_pObject = fragment;
	}
	struct GnomeCanvasPangoSelBounds bounds;
	bool need_update = false;
	gcpFragment *pFragment = NULL;
	if (m_pObject) {
		switch(m_pObject->GetType ()) {
			case AtomType: {
				gcpAtom* pAtom = (gcpAtom*) m_pObject;
				if (pAtom->GetTotalBondsNumber () > 1)
					return false;
				double x, y;
				pAtom->GetCoords (&x, &y);
				gcpMolecule *pMol = (gcpMolecule*) pAtom->GetMolecule ();
				map<Atom*, Bond*>::iterator i;
				gcpBond *pBond = (gcpBond*) pAtom->GetFirstBond (i);
				pFragment = new gcpFragment (x, y);
				gcpAtom* pFragAtom = (gcpAtom*) pFragment->m_Atom;
				map<string, Object*>::iterator ie;
				Object* electron = pAtom->GetFirstChild (ie);
				while (electron) {
					m_pView->Remove (electron);
					delete electron;
					electron = pAtom->GetNextChild (ie);
				}
				pMol->Remove (pAtom);
				pAtom->SetParent (NULL);
				pMol->AddFragment (pFragment);
				pDoc->AddFragment (pFragment);
				pDoc->AbortOperation ();
				gcpOperation* pOp = pDoc->GetNewOperation (GCP_MODIFY_OPERATION);
				pOp->AddObject (pAtom, 0);
				if (pBond)
					pOp->AddObject (pBond, 0);
				m_pView->Remove (pAtom);
				pFragAtom->SetZ (pAtom->GetZ ());
				pFragAtom->SetId ((gchar*) pAtom->GetId ());
				int n = pAtom->GetAttachedHydrogens ();
				if (n) {
					char* buf;
					if (n > 1)
						buf = g_strdup_printf ("H%d", n);
					else
						buf = g_strdup ("H");
					bounds.start = bounds.cur = ((pAtom->GetBestSide ())? strlen (pAtom->GetSymbol ()): 0);
					pFragment->OnSelChanged (&bounds);
					gcp_pango_layout_replace_text (pFragment->GetLayout (),
						bounds.cur,
						0, buf, pDoc->GetPangoAttrList ());
					bounds.cur +=  strlen (buf);
					need_update = true;
					g_free (buf);
				}
				delete pAtom;
				if (pBond) {
					pBond->ReplaceAtom (pAtom, pFragAtom);
					pFragAtom->AddBond (pBond);
					pOp->AddObject (pBond, 1);
				}
				pOp->AddObject (pFragment, 1);
				pDoc->FinishOperation ();
				pDoc->EmptyTranslationTable ();
				m_pObject = pFragment;
				break;
			}
			case BondType:
				return false;
			case FragmentType:
				break;
			default:
				return false;
		}
		m_pObject->SetSelected (m_pWidget, SelStateUpdating);
		m_Active = GNOME_CANVAS_PANGO (g_object_get_data (G_OBJECT (m_pData->Items[m_pObject]), "fragment"));
		if (need_update) {
			gnome_canvas_pango_set_selection_bounds (m_Active,  bounds.cur,  bounds.cur);
			pFragment->AnalContent ((unsigned) bounds.start, (unsigned&) bounds.cur);
			pFragment->OnChanged (false);
		}
		m_pView->SetGnomeCanvasPangoActive (m_Active);
		g_object_set (G_OBJECT (m_Active), "editing", true, NULL);
		m_CurNode = ((gcpFragment*) m_pObject)->SaveSelected ();
		m_InitNode = ((gcpFragment*) m_pObject)->SaveSelected ();
		pDoc->GetWindow ()->ActivateActionWidget ("/MainMenu/FileMenu/SaveAsImage", false);
	}
	return true;
}

bool gcpFragmentTool::Deactivate ()
{
	if (m_Active && !Unselect ())
		return false;
	return true;
}

void gcpFragmentTool::Activate ()
{
}

bool gcpFragmentTool::OnEvent (GdkEvent* event)
{
	if (m_Active) {
		if ((event->type == GDK_KEY_PRESS) || (event->type == GDK_KEY_RELEASE)) {
			if (event->key.state & GDK_CONTROL_MASK) {
				switch(event->key.keyval) {
					case GDK_Right:
					case GDK_Left:
					case GDK_Up:
					case GDK_Down:
					case GDK_End:
					case GDK_Home:
					case GDK_Delete:
					case GDK_KP_Delete:
					case GDK_BackSpace:
					break;
					case GDK_z:
						m_pView->GetDoc ()->OnUndo ();
						return true;
					case GDK_Z:
						m_pView->GetDoc ()->OnRedo ();
						return true;
					case GDK_c:
						CopySelection (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
						return true;
					case GDK_v:
						PasteSelection (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
						return true;
					case GDK_x:
						CutSelection (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
						return true;
					default:
						return false;
				}
			}
			if (event->key.keyval == GDK_KP_Enter || event->key.keyval == GDK_Return ||
								event->key.keyval == GDK_space)
				return true;
			if (!g_utf8_validate (((GdkEventKey*) event)->string, -1, NULL)) {
				gsize r, w;
				gchar* newstr = g_locale_to_utf8 (((GdkEventKey*) event)->string, ((GdkEventKey*) event)->length, &r, &w, NULL);
				g_free (((GdkEventKey*) event)->string);
				((GdkEventKey*) event)->string = newstr;
				((GdkEventKey*) event)->length = w;
			}
			gnome_canvas_item_grab_focus ((GnomeCanvasItem*) m_Active);
			GnomeCanvasItemClass* klass = GNOME_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (m_Active));
			klass->event ((GnomeCanvasItem*) m_Active, event);
			return true;
		}
	}
	return false;
}

bool gcpFragmentTool::CopySelection (GtkClipboard *clipboard)
{
	if (!m_Active)
		return false;
	unsigned start, end;
	gcpFragment *fragment = (gcpFragment*) g_object_get_data (G_OBJECT (m_Active), "object");
	fragment->GetSelectionBounds (start, end);
	if (start == end)
		return false;
	m_pData->Copy (clipboard); //To clean the xmlDoc
	xmlDocPtr pDoc = m_pData->GetXmlDoc (clipboard);
	if (!pDoc)
		return false;
	pDoc->children = xmlNewDocNode (pDoc, NULL, (xmlChar*) "chemistry", NULL);
	xmlNsPtr ns = xmlNewNs (pDoc->children, (xmlChar*) "http://www.nongnu.org/gchempaint", (xmlChar*) "gcp");
	xmlSetNs (pDoc->children, ns);
	xmlNodePtr node = fragment->SaveSelection (pDoc);
	if (node)
		xmlAddChild (pDoc->children, node);
	else
		return false;
	gtk_clipboard_set_with_data (clipboard, targets, ClipboardFormats,
				(GtkClipboardGetFunc) on_get_data,
				(GtkClipboardClearFunc) on_clear_data, this);
	gtk_clipboard_request_contents (clipboard,
			gdk_atom_intern ("TARGETS", FALSE),
			(GtkClipboardReceivedFunc) on_receive_targets,
			m_pApp);
	return true;
}

bool gcpFragmentTool::CutSelection (GtkClipboard *clipboard)
{
	if (!CopySelection (clipboard))
		return false;
	return DeleteSelection ();
}

/**
* This method does nothing and always return false because pasting something inside
* a fragment is somewhat unsafe and will not be implemented in a foreseable future.
*/
bool gcpFragmentTool::OnReceive (GtkClipboard *clipboard, GtkSelectionData *data, int type)
{
	return false;
}

bool gcpFragmentTool::Unselect ()
{
	if (!m_Active)
		return true;
	gcpFragment *fragment = (gcpFragment*) g_object_get_data (G_OBJECT (m_Active), "object");
	if (fragment->Validate ())
		return gcpTextTool::Unselect ();
	return false;
}

void gcpFragmentTool::OnGetData (GtkClipboard *clipboard, GtkSelectionData *selection_data,  guint info)
{
	xmlDocPtr pDoc = gcpWidgetData::GetXmlDoc (clipboard);
	if (ClipboardData) {
		xmlFree (ClipboardData);
		ClipboardData = NULL;
	} 
	ClipboardDataType = info;
	gint size;
	if (info) {
		ClipboardData = xmlNodeGetContent (pDoc->children->children);
		size = strlen ((char*) ClipboardData);
		gtk_selection_data_set_text (selection_data, (const gchar*) ClipboardData, size);
	} else {
		xmlDocDumpFormatMemory (pDoc, &ClipboardData, &size, info);
		gtk_selection_data_set (selection_data, gdk_atom_intern (GCHEMPAINT_ATOM_NAME, FALSE), 8,  (const guchar*) ClipboardData, size);
	}
	cleared = false;
	if (clipboard == gtk_clipboard_get (GDK_SELECTION_CLIPBOARD))
		m_pApp->ActivateWindowsActionWidget ("/MainMenu/EditMenu/Paste", true);
}


syntax highlighted by Code2HTML, v. 0.9.1