// -*- C++ -*- /* * GChemPaint library * text.cc * * Copyright (C) 2002-2006 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 "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 #include #include 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 (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 (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 (); }