// -*- C++ -*-
/*
* GChemPaint library
* text.cc
*
* Copyright (C) 2002-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 "text.h"
#include "widgetdata.h"
#include "view.h"
#include "document.h"
#include "application.h"
#include "settings.h"
#include "theme.h"
#include "window.h"
#include "libgcpcanvas/gcp-canvas-group.h"
#include "libgcpcanvas/gcp-canvas-rect-ellipse.h"
#include "libgcpcanvas/gprintable.h"
#include <glib/gi18n-lib.h>
#include <stdexcept>
#include <cstring>
static void on_text_changed (gcpText *text)
{
text->OnChanged (true);
}
static void on_text_sel_changed (gcpText *text, struct GnomeCanvasPangoSelBounds *bounds)
{
text->OnSelChanged (bounds);
}
gcpText::gcpText(): gcpTextObject(TextType)
{
}
gcpText::gcpText(double x, double y): gcpTextObject(x, y, TextType)
{
}
gcpText::~gcpText()
{
}
void gcpText::GetCoords(double *x, double *y)
{
*x = m_x;
*y = m_y;
}
void gcpText::SetCoords(double x, double y)
{
m_x = x;
m_y = y;
}
class SaveStruct {
public:
SaveStruct (PangoAttribute *attribute);
~SaveStruct ();
SaveStruct *next, *children;
PangoAttribute *attr;
};
SaveStruct::SaveStruct (PangoAttribute *attribute)
{
attr = pango_attribute_copy (attribute);
next = children = NULL;
}
SaveStruct::~SaveStruct ()
{
pango_attribute_destroy (attr);
if (children)
delete children;
if (next)
delete next;
}
static bool save_state (xmlDocPtr xml, xmlNodePtr node, char const *t, SaveStruct *s, unsigned index, int es, int ef, char const *f, int n)
{
xmlNodePtr child = NULL;
switch (s->attr->klass->type) {
case PANGO_ATTR_FAMILY:
f = ((PangoAttrString*) s->attr)->value;
ef = s->attr->end_index;
if (es >= ef) {
char *str = g_strdup_printf ("%s %g", f, (double) n / PANGO_SCALE);
child = xmlNewDocNode (xml, NULL, (xmlChar*) "font", NULL);
xmlNewProp (child, (xmlChar*) "name", (xmlChar*) str);
g_free (str);
xmlAddChild (node, child);
}
break;
case PANGO_ATTR_STYLE: {
PangoStyle st = (PangoStyle) ((PangoAttrInt*) s->attr)->value;
if (st == PANGO_STYLE_NORMAL)
break;
child = xmlNewDocNode (xml, NULL, (xmlChar*) "i", NULL);
if (st == PANGO_STYLE_OBLIQUE)
xmlNewProp (child, (xmlChar*) "style", (xmlChar*) "oblique");
xmlAddChild (node, child);
break;
}
case PANGO_ATTR_WEIGHT: {
PangoWeight w = (PangoWeight) ((PangoAttrInt*) s->attr)->value;
if (w == PANGO_WEIGHT_NORMAL)
break;
child = xmlNewDocNode (xml, NULL, (xmlChar*) "b", NULL);
if (w != PANGO_WEIGHT_BOLD) {
char *buf = g_strdup_printf ("%d", (int) w / 100);
xmlNewProp (child, (xmlChar*) "weight", (xmlChar*) buf);
g_free (buf);
}
xmlAddChild (node, child);
break;
}
case PANGO_ATTR_VARIANT: {
PangoVariant v = (PangoVariant) ((PangoAttrInt*) s->attr)->value;
if (v == PANGO_VARIANT_SMALL_CAPS) {
child = xmlNewDocNode (xml, NULL, (xmlChar*) "small-caps", NULL);
xmlAddChild (node, child);
}
break;
}
case PANGO_ATTR_STRETCH: {
PangoStretch st = (PangoStretch) ((PangoAttrInt*) s->attr)->value;
char *stretch = NULL;
switch (st) {
case PANGO_STRETCH_ULTRA_CONDENSED:
stretch = (char*) "ultra-condensed";
break;
case PANGO_STRETCH_EXTRA_CONDENSED:
stretch = (char*) "extra-condensed";
break;
case PANGO_STRETCH_CONDENSED:
stretch = (char*) "condensed";
break;
case PANGO_STRETCH_SEMI_CONDENSED:
stretch = (char*) "semi-condensed";
break;
case PANGO_STRETCH_NORMAL:
break;
case PANGO_STRETCH_SEMI_EXPANDED:
stretch = (char*) "semi-expanded";
break;
case PANGO_STRETCH_EXPANDED:
stretch = (char*) "expanded";
break;
case PANGO_STRETCH_EXTRA_EXPANDED:
stretch = (char*) "extra-expanded";
break;
case PANGO_STRETCH_ULTRA_EXPANDED:
stretch = (char*) "ultra-expanded";
break;
}
if (!stretch)
break;
child = xmlNewDocNode (xml, NULL, (xmlChar*) "stretch", NULL);
xmlNewProp (child, (xmlChar*) "type", (xmlChar*) stretch);
xmlAddChild (node, child);
break;
}
case PANGO_ATTR_SIZE:
n = ((PangoAttrInt*) s->attr)->value;
es = s->attr->end_index;
if (ef >= es) {
char *str = g_strdup_printf ("%s %g", f, (double) n / PANGO_SCALE);
child = xmlNewDocNode (xml, NULL, (xmlChar*) "font", NULL);
xmlNewProp (child, (xmlChar*) "name", (xmlChar*) str);
g_free (str);
xmlAddChild (node, child);
}
break;
case PANGO_ATTR_UNDERLINE: {
PangoUnderline u = (PangoUnderline) ((PangoAttrInt*) s->attr)->value;
char *type = NULL;
switch (u) {
case PANGO_UNDERLINE_NONE:
case PANGO_UNDERLINE_SINGLE:
break;
case PANGO_UNDERLINE_DOUBLE:
type = (char*) "double";
break;
case PANGO_UNDERLINE_LOW:
type = (char*) "low";
break;
case PANGO_UNDERLINE_ERROR:
type = (char*) "error"; // not really implemented
break;
}
if (u == PANGO_UNDERLINE_NONE)
break;
child = xmlNewDocNode (xml, NULL, (xmlChar*) "u", NULL);
if (u != PANGO_UNDERLINE_SINGLE)
xmlNewProp (child, (xmlChar*) "type", (xmlChar*) type);
xmlAddChild (node, child);
break;
}
case PANGO_ATTR_STRIKETHROUGH:
if (((PangoAttrInt*) s->attr)->value) {
child = xmlNewDocNode (xml, NULL, (xmlChar*) "s", NULL);
xmlAddChild (node, child);
}
break;
case PANGO_ATTR_RISE: {
int rise = ((PangoAttrInt*) s->attr)->value / PANGO_SCALE;
if (rise != 0) {
child = xmlNewDocNode (xml, NULL, (xmlChar*) ((rise > 0)? "sup": "sub"), NULL);
char *buf = g_strdup_printf ("%d", abs (rise));
xmlNewProp (child, (xmlChar*) "height", (xmlChar*) buf);
g_free (buf);
xmlAddChild (node, child);
}
break;
}
case PANGO_ATTR_FOREGROUND: {
PangoAttrColor *c = (PangoAttrColor*) s->attr;
if (c->color.red == 0 && c->color.green == 0 && c->color.blue == 0)
break;
child = xmlNewDocNode (xml, NULL, (xmlChar*) "fore", NULL);
char *buf = g_strdup_printf ("%g", (double) c->color.red / 0xffff);
xmlNewProp (child, (xmlChar*) "red", (xmlChar*) buf);
g_free (buf);
buf = g_strdup_printf ("%g", (double) c->color.green / 0xffff);
xmlNewProp (child, (xmlChar*) "green", (xmlChar*) buf);
g_free (buf);
buf = g_strdup_printf ("%g", (double) c->color.blue / 0xffff);
xmlNewProp (child, (xmlChar*) "blue", (xmlChar*) buf);
g_free (buf);
xmlAddChild (node, child);
break;
}
default:
break;
}
if (!child)
child = node;
if (s->children) {
SaveStruct *s0 = s->children;
while (s0) {
if (s0->attr->start_index > index) {
xmlNodeAddContentLen (child, (xmlChar const*) (t + index), s0->attr->start_index - index);
index = s0->attr->start_index;
}
save_state (xml, child, t, s0, index, es, ef, f, n);
index = s0->attr->end_index;
s0 = s0->next;
}
if (s->attr->end_index > index)
xmlNodeAddContentLen (child, (xmlChar const*) (t + index), s->attr->end_index - index);
} else {
xmlNodeAddContentLen (child, (xmlChar const*) (t + s->attr->start_index), s->attr->end_index - s->attr->start_index);
}
return true;
}
bool filter_func (PangoAttribute *attribute, SaveStruct **cur_state)
{
if (!*cur_state) {
*cur_state = new SaveStruct (attribute);
return false;
}
if (attribute->start_index < (*cur_state)->attr->start_index) {
throw logic_error (_("This should not have occured, please file a bug record."));
} else if (attribute->start_index == (*cur_state)->attr->start_index) {
if (attribute->end_index <= (*cur_state)->attr->end_index)
filter_func (attribute, &(*cur_state)->children);
else {
if ((*cur_state)->next) {
throw logic_error (_("This should not have occured, please file a bug record."));
} else {
// in that case, just set the new attribute as parent of the old one
SaveStruct *s = *cur_state;
*cur_state = new SaveStruct (attribute);
(*cur_state)->children = s;
}
}
} else {
if (attribute->start_index >= (*cur_state)->attr->end_index)
filter_func (attribute, &(*cur_state)->next);
else if (attribute->end_index <= (*cur_state)->attr->end_index)
filter_func (attribute, &(*cur_state)->children);
else {
PangoAttribute *attr = pango_attribute_copy (attribute),
*attr1 = pango_attribute_copy (attribute);
attr->start_index = attr1->end_index = (*cur_state)->attr->end_index;
filter_func (attr1, &(*cur_state)->children);
filter_func (attr, &(*cur_state)->next);
pango_attribute_destroy (attr);
pango_attribute_destroy (attr1);
}
}
return false;
}
xmlNodePtr gcpText::Save(xmlDocPtr xml)
{
xmlNodePtr node = xmlNewDocNode (xml, NULL, (xmlChar*) "text", NULL);
if (!node)
return NULL;
if (!gcpTextObject::SaveNode (xml, node)) {
xmlFreeNode (node);
return NULL;
}
unsigned i = 0;
SaveStruct *head = NULL, *cur;
const char *text = pango_layout_get_text (m_Layout);
PangoAttrList *l = pango_layout_get_attributes (m_Layout);
pango_attr_list_filter (l, (PangoAttrFilterFunc) filter_func, &head);
cur = head;
while (cur) {
save_state (xml, node, text, cur, i, 0, 0, NULL, 0);
i = cur->attr->end_index;
cur = cur->next;
}
xmlNodeAddContent (node, (xmlChar*) text + i);
delete head;
return node;
}
struct SelState {
unsigned start, end;
PangoAttrList *l;
};
bool selection_filter_func (PangoAttribute *attribute, struct SelState *state)
{
if (attribute->start_index < state->end && attribute ->end_index > state->start) {
PangoAttribute *attr = pango_attribute_copy (attribute);
attr->start_index = (attribute->start_index < state->start)? 0: attribute->start_index - state->start;
attr->end_index = (attribute->end_index > state->end)? state->end - state->start: attribute->end_index - state->start;
pango_attr_list_insert (state->l, attr);
}
return false;
}
xmlNodePtr gcpText::SaveSelection(xmlDocPtr xml)
{
xmlNodePtr node = xmlNewDocNode (xml, NULL, (xmlChar*) "text", NULL);
if (!node)
return NULL;
// get the text and attributes
const char *text = pango_layout_get_text (m_Layout);
PangoAttrList *l = pango_layout_get_attributes (m_Layout);
string selection (text + m_StartSel, m_EndSel - m_StartSel);
// make a new filtered attributes list
struct SelState state;
state.start = m_StartSel;
state.end = m_EndSel;
state.l = pango_attr_list_new ();
pango_attr_list_filter (l, (PangoAttrFilterFunc) selection_filter_func, &state);
unsigned i = 0;
SaveStruct *head = NULL, *cur;
pango_attr_list_filter (state.l, (PangoAttrFilterFunc) filter_func, &head);
cur = head;
while (cur) {
save_state (xml, node, selection.c_str (), cur, i, 0, 0, NULL, 0);
i = cur->attr->end_index;
cur = cur->next;
}
delete head;
pango_attr_list_unref (state.l);
return (gcpTextObject::SaveNode (xml, node))? node: NULL;
}
bool gcpText::Load (xmlNodePtr node)
{
if (!gcpTextObject::Load (node))
return false;
xmlNodePtr child;
m_bLoading = true;
child = node->children;
if (m_AttrList)
pango_attr_list_unref (m_AttrList);
m_buf.clear ();
m_AttrList = pango_attr_list_new ();
unsigned pos = 0;
while (child) {
if (!LoadNode (child, pos, 1))
return false;
child = child->next;
}
if (m_Layout) {
pango_layout_set_text (m_Layout, m_buf.c_str (), -1);
pango_layout_set_attributes (m_Layout, m_AttrList);
pango_attr_list_unref (m_AttrList);
m_AttrList = NULL;
}
m_bLoading = false;
return true;
}
bool gcpText::LoadSelection (xmlNodePtr node, unsigned pos)
{
xmlNodePtr child;
m_bLoading = true;
m_buf = pango_layout_get_text (m_Layout);
m_AttrList = pango_layout_get_attributes (m_Layout);
child = node->children;
while (child) {
if (!LoadNode (child, pos, 1))
return false;
child = child->next;
}
pango_layout_set_text (m_Layout, m_buf.c_str (), -1);
pango_layout_set_attributes (m_Layout, m_AttrList);
GtkWidget* pWidget = dynamic_cast<gcpDocument*> (GetDocument ())->GetWidget ();
gcpWidgetData* pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (pWidget), "data");
GnomeCanvasGroup *group = pData->Items[this];
if (group) {
GnomeCanvasPango *PangoItem = GNOME_CANVAS_PANGO (g_object_get_data (G_OBJECT (group), "text"));
gnome_canvas_pango_set_selection_bounds (PangoItem, pos, pos);
}
m_bLoading = false;
OnChanged (true);
return true;
}
struct limits {
unsigned start, length;
};
static bool on_insert (PangoAttribute *attr, struct limits *l)
{
if (attr->start_index > l->start) {
attr->start_index += l->length;
attr->end_index += l->length;
} else if (attr->end_index > l->start)
attr->end_index += l->length;
return false;
}
bool gcpText::LoadNode (xmlNodePtr node, unsigned &pos, int level)
{
char* buf;
PangoAttribute *Attr = NULL, *Attr0 = NULL;
unsigned start = pos;
if (!strcmp ((const char*) node->name, "text")) {
if (!level)
return true;
buf = (char*) xmlNodeGetContent (node);
if (buf) {
struct limits l;
l.start = start;
pos += strlen (buf);
l.length = pos - start;
pango_attr_list_filter (m_AttrList, (PangoAttrFilterFunc) on_insert, &l);
m_buf.insert (start, buf);
xmlFree (buf);
}
} else if (!strcmp ((const char*) node->name, "br")) {
m_buf.insert (pos, "\n");
pos++;
struct limits l;
l.start = start;
l.length = 1;
pango_attr_list_filter (m_AttrList, (PangoAttrFilterFunc) on_insert, &l);
} else if (!strcmp ((const char*) node->name, "b")) {
PangoWeight weight = PANGO_WEIGHT_BOLD;
buf = (char*) xmlGetProp(node, (xmlChar*) "weight");
if (buf) {
weight = (PangoWeight) (strtol (buf, NULL, 10) * 100);
xmlFree (buf);
}
Attr = pango_attr_weight_new (weight);
} else if (!strcmp ((const char*) node->name, "i")) {
PangoStyle style = PANGO_STYLE_ITALIC;
buf = (char*) xmlGetProp(node, (xmlChar*) "style");
if (buf) {
if (!strcmp (buf, "oblique"))
style = PANGO_STYLE_OBLIQUE;
xmlFree (buf);
}
Attr = pango_attr_style_new (style);
} else if (!strcmp ((const char*) node->name, "u")) {
PangoUnderline underline = PANGO_UNDERLINE_SINGLE;
buf = (char*) xmlGetProp(node, (xmlChar*) "type");
if (buf) {
if (!strcmp (buf, "double"))
underline = PANGO_UNDERLINE_DOUBLE;
else if (!strcmp (buf, "low"))
underline = PANGO_UNDERLINE_LOW;
else if (!strcmp (buf, "error"))
underline = PANGO_UNDERLINE_ERROR;
xmlFree (buf);
}
Attr = pango_attr_underline_new (underline);
} else if (!strcmp ((const char*) node->name, "s"))
Attr = pango_attr_strikethrough_new (true);
else if (!strcmp ((const char*) node->name, "sub")) {
buf = (char*) xmlGetProp (node, (xmlChar*) "height");
if (!buf)
return false;
int rise = -strtoul (buf, NULL, 10) * PANGO_SCALE;
xmlFree (buf);
Attr = pango_attr_rise_new (rise);
} else if (!strcmp ((const char*) node->name, "sup")) {
buf = (char*) xmlGetProp (node, (xmlChar*) "height");
if (!buf)
return false;
int rise = strtoul (buf, NULL, 10) * PANGO_SCALE;
xmlFree (buf);
Attr = pango_attr_rise_new (rise);
} else if (!strcmp ((const char*) node->name, "font")) {
char* TagName = (char*) xmlGetProp (node, (xmlChar*) "name");
if (!TagName)
return false;
PangoFontDescription* pfd =pango_font_description_from_string (TagName);
Attr = pango_attr_family_new (pango_font_description_get_family (pfd));
Attr0 = pango_attr_size_new (pango_font_description_get_size (pfd));
pango_font_description_free (pfd);
xmlFree (TagName);
} else if (!strcmp ((const char*) node->name, "small-caps"))
Attr = pango_attr_variant_new (PANGO_VARIANT_SMALL_CAPS);
else if (!strcmp ((const char*) node->name, "stretch")) {
buf = (char*) xmlGetProp(node, (xmlChar*) "type");
if (!buf)
return false;
PangoStretch stretch = PANGO_STRETCH_NORMAL;
if (!strcmp (buf, "ultra-condensed"))
stretch = PANGO_STRETCH_ULTRA_CONDENSED;
else if(!strcmp (buf, "extra-condensed"))
stretch = PANGO_STRETCH_EXTRA_CONDENSED;
else if(!strcmp (buf, "condensed"))
stretch = PANGO_STRETCH_CONDENSED;
else if(!strcmp (buf, "semi-condensed"))
stretch = PANGO_STRETCH_SEMI_CONDENSED;
else if(!strcmp (buf, "semi-expanded"))
stretch = PANGO_STRETCH_SEMI_EXPANDED;
else if(!strcmp (buf, "expanded"))
stretch = PANGO_STRETCH_EXPANDED;
else if(!strcmp (buf, "extra-expanded"))
stretch = PANGO_STRETCH_EXTRA_EXPANDED;
else if(!strcmp (buf, "ultra-expanded"))
stretch = PANGO_STRETCH_ULTRA_EXPANDED;
xmlFree (buf);
Attr = pango_attr_stretch_new (stretch);
} else if (!strcmp ((const char*) node->name, "fore")) {
guint16 red, green, blue;
buf = (char*) xmlGetProp(node, (xmlChar*) "red");
if (!buf)
return false;
red = (guint16) (strtod (buf, NULL) * 0xffff);
xmlFree (buf);
buf = (char*) xmlGetProp(node, (xmlChar*) "green");
if (!buf)
return false;
green = (guint16) (strtod (buf, NULL) * 0xffff);
xmlFree (buf);
buf = (char*) xmlGetProp(node, (xmlChar*) "blue");
if (!buf)
return false;
blue = (guint16) (strtod (buf, NULL) * 0xffff);
xmlFree (buf);
Attr = pango_attr_foreground_new (red, green, blue);
} else
return true;
xmlNodePtr child = node->children;
while (child) {
if (!LoadNode (child, pos, 1))
return false;
child = child->next;
}
if (Attr) {
Attr->start_index = start;
Attr->end_index = pos;
pango_attr_list_change (m_AttrList, Attr);
}
if (Attr0) {
Attr0->start_index = start;
Attr0->end_index = pos;
pango_attr_list_change (m_AttrList, Attr0);
}
return true;
}
void gcpText::Add (GtkWidget* w)
{
gcpWidgetData* pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (w), "data");
gcpTheme *pTheme = pData->View->GetDoc ()->GetTheme ();
if (m_ascent <= 0) {
PangoContext* pc = pData->View->GetPangoContext ();
m_Layout = pango_layout_new (pc);
PangoAttrList *l = pango_attr_list_new ();
pango_layout_set_attributes (m_Layout, l);
PangoFontDescription *desc = pango_font_description_new ();
pango_font_description_set_family (desc, pData->View->GetDoc ()->GetTextFontFamily ());
pango_font_description_set_style (desc, pData->View->GetDoc ()->GetTextFontStyle ());
pango_font_description_set_variant (desc, pData->View->GetDoc ()->GetTextFontVariant ());
pango_font_description_set_weight (desc, pData->View->GetDoc ()->GetTextFontWeight ());
pango_font_description_set_size (desc, pData->View->GetDoc ()->GetTextFontSize ());
pango_layout_set_font_description (m_Layout, desc);
pango_font_description_free (desc);
pango_layout_set_text (m_Layout, "l", -1);
PangoLayoutIter* iter = pango_layout_get_iter (m_Layout);
m_ascent = pango_layout_iter_get_baseline (iter) / PANGO_SCALE;
pango_layout_iter_free (iter);
pango_layout_set_text (m_Layout, m_buf.c_str (), -1);
m_buf.clear ();
if (m_AttrList) {
pango_layout_set_attributes (m_Layout, m_AttrList);
pango_attr_list_unref (m_AttrList);
m_AttrList = NULL;
}
PangoRectangle rect;
pango_layout_get_extents (m_Layout, NULL, &rect);
m_length = rect.width / PANGO_SCALE;
m_height = rect.height / PANGO_SCALE;
}
GnomeCanvasGroup* group = GNOME_CANVAS_GROUP (gnome_canvas_item_new (pData->Group, gnome_canvas_group_ext_get_type(), NULL));
GnomeCanvasItem* item = gnome_canvas_item_new (
group,
gnome_canvas_rect_ext_get_type (),
"x1", m_x * pTheme->GetZoomFactor () - pTheme->GetPadding (),
"y1", m_y * pTheme->GetZoomFactor () - pTheme->GetPadding () - m_ascent,
"x2", m_x * pTheme->GetZoomFactor () + m_length + pTheme->GetPadding (),
"y2", m_y * pTheme->GetZoomFactor () + m_height + pTheme->GetPadding () - m_ascent,
"fill_color", "white",
"outline_color", "white",
NULL);
g_object_set_data (G_OBJECT (group), "rect", item);
g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w);
g_object_set_data (G_OBJECT (item), "object", this);
item = gnome_canvas_item_new (
group,
gnome_canvas_pango_get_type (),
"layout", m_Layout,
"x", m_x * pTheme->GetZoomFactor (),
"y", m_y * pTheme->GetZoomFactor () - m_ascent,
"editing", false,
NULL);
g_object_set_data (G_OBJECT (group), "text", item);
g_object_set_data (G_OBJECT (item), "object", this);
g_signal_connect (G_OBJECT (item), "event", G_CALLBACK (on_event), w);
g_signal_connect_swapped (G_OBJECT(item), "changed", G_CALLBACK (on_text_changed), this);
g_signal_connect_swapped (G_OBJECT (item), "sel-changed", G_CALLBACK (on_text_sel_changed), this);
pData->Items[this] = group;
}
bool gcpText::OnChanged (bool save)
{
gcpDocument* pDoc = (gcpDocument*) GetDocument ();
if (!pDoc)
return false;
gcpView* pView = pDoc->GetView ();
GtkWidget* pWidget = pView->GetWidget ();
gcpWidgetData* pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (pWidget), "data");
GnomeCanvasGroup *group = pData->Items[this];
if (!group) {
pData->Items.erase (this);
return false;
}
if (strlen (pango_layout_get_text (m_Layout))) {
PangoLayoutIter* iter = pango_layout_get_iter (m_Layout);
m_ascent = pango_layout_iter_get_baseline (iter) / PANGO_SCALE;
pango_layout_iter_free (iter);
}
PangoRectangle rect;
pango_layout_get_extents (m_Layout, NULL, &rect);
m_length = rect.width / PANGO_SCALE;
m_height = rect.height / PANGO_SCALE;
pView->Update (this);
EmitSignal (OnChangedSignal);
GnomeCanvasPango *PangoItem = GNOME_CANVAS_PANGO (g_object_get_data (G_OBJECT (group), "text"));
unsigned CurPos = gnome_canvas_pango_get_cur_index (PangoItem);
m_StartSel = m_EndSel = CurPos;
if (save) {
gcpTool* TextTool = dynamic_cast<gcpApplication*> (pDoc->GetApplication ())->GetTool ("Text");
if (!TextTool)
return true;
xmlNodePtr node = SaveSelected ();
if (node)
TextTool->PushNode (node);
}
return true;
}
void gcpText::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];
g_object_set(G_OBJECT(g_object_get_data(G_OBJECT(group), "text")),
"x", m_x * pTheme->GetZoomFactor (),
"y", m_y * pTheme->GetZoomFactor () - m_ascent,
"width", m_length,
"height", m_height,
NULL);
g_object_set(G_OBJECT(g_object_get_data(G_OBJECT(group), "rect")),
"x1", m_x * pTheme->GetZoomFactor () - pTheme->GetPadding (),
"y1", m_y * pTheme->GetZoomFactor () - pTheme->GetPadding () - m_ascent,
"x2", m_x * pTheme->GetZoomFactor () + m_length + pTheme->GetPadding (),
"y2", m_y * pTheme->GetZoomFactor () + m_height + pTheme->GetPadding () - m_ascent,
NULL);
}
void gcpText::SetSelected(GtkWidget* w, int state)
{
gcpWidgetData* pData = (gcpWidgetData*)g_object_get_data(G_OBJECT(w), "data");
GnomeCanvasGroup* group = pData->Items[this];
gchar* color;
switch (state) {
case SelStateUnselected:
color = (char*) "white";
break;
case SelStateSelected:
color = SelectColor;
break;
case SelStateUpdating:
color = AddColor;
break;
case SelStateErasing:
color = DeleteColor;
break;
default:
color = (char*) "white";
break;
}
g_object_set(G_OBJECT(g_object_get_data(G_OBJECT(group), "rect")), "outline_color", color, NULL);
}
bool gcpText::OnEvent(GdkEvent *event)
{
if ((event->type == GDK_BUTTON_PRESS) && (event->button.button == 2))
{
return true;
}
else return false;
}
void gcpText::Transform2D(Matrix2D& m, double x, double y)
{
m_x += m_length / 2 - x;
m_y += m_height / 2 - m_ascent + - y;
m.Transform (m_x, m_y);
m_x -= m_length / 2 - x;
m_y -= m_height / 2 - m_ascent - y;
}
double gcpText::GetYAlign ()
{
return m_y - ((gcpDocument*) GetDocument ())->GetView ()->GetBaseLineOffset ();
}
syntax highlighted by Code2HTML, v. 0.9.1