1 /** @file
2 * @brief SVG Fonts dialog - implementation
3 */
4 /* Authors:
5 * Felipe C. da S. Sanches <juca@members.fsf.org>
6 *
7 * Copyright (C) 2008 Authors
8 * Released under GNU GPLv2 (or later). Read the file 'COPYING' for more information.
9 */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
15 #ifdef ENABLE_SVG_FONTS
17 #include <2geom/pathvector.h>
18 #include "document-private.h"
19 #include <gtkmm/notebook.h>
20 #include <glibmm/i18n.h>
21 #include <message-stack.h>
22 #include "selection.h"
23 #include <string.h>
24 #include "svg/svg.h"
25 #include "svg-fonts-dialog.h"
26 #include "xml/node.h"
27 #include "xml/repr.h"
29 SvgFontDrawingArea::SvgFontDrawingArea(){
30 this->text = "";
31 this->svgfont = NULL;
32 }
34 void SvgFontDrawingArea::set_svgfont(SvgFont* svgfont){
35 this->svgfont = svgfont;
36 }
38 void SvgFontDrawingArea::set_text(Glib::ustring text){
39 this->text = text;
40 redraw();
41 }
43 void SvgFontDrawingArea::set_size(int x, int y){
44 this->x = x;
45 this->y = y;
46 ((Gtk::Widget*) this)->set_size_request(x, y);
47 }
49 void SvgFontDrawingArea::redraw(){
50 ((Gtk::Widget*) this)->queue_draw();
51 }
53 bool SvgFontDrawingArea::on_expose_event (GdkEventExpose */*event*/){
54 if (this->svgfont){
55 Glib::RefPtr<Gdk::Window> window = get_window();
56 Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
57 cr->set_font_face( Cairo::RefPtr<Cairo::FontFace>(new Cairo::FontFace(this->svgfont->get_font_face(), false /* does not have reference */)) );
58 cr->set_font_size (this->y-20);
59 cr->move_to (10, 10);
60 cr->show_text (this->text.c_str());
61 }
62 return TRUE;
63 }
65 namespace Inkscape {
66 namespace UI {
67 namespace Dialog {
69 /*
70 Gtk::HBox* SvgFontsDialog::AttrEntry(gchar* lbl, const SPAttributeEnum attr){
71 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
72 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
73 Gtk::Entry* entry = Gtk::manage(new Gtk::Entry());
74 hbox->add(* entry );
75 hbox->show_all();
77 entry->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_attr_changed));
78 return hbox;
79 }
80 */
82 SvgFontsDialog::AttrEntry::AttrEntry(SvgFontsDialog* d, gchar* lbl, const SPAttributeEnum attr){
83 this->dialog = d;
84 this->attr = attr;
85 this->add(* Gtk::manage(new Gtk::Label(lbl)) );
86 this->add(entry);
87 this->show_all();
89 entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::AttrEntry::on_attr_changed));
90 }
92 void SvgFontsDialog::AttrEntry::set_text(char* t){
93 if (!t) return;
94 entry.set_text(t);
95 }
97 void SvgFontsDialog::AttrEntry::on_attr_changed(){
99 SPObject* o = NULL;
100 for(SPObject* node = this->dialog->get_selected_spfont()->children; node; node=node->next){
101 switch(this->attr){
102 case SP_PROP_FONT_FAMILY:
103 if (SP_IS_FONTFACE(node)){
104 o = node;
105 continue;
106 }
107 break;
108 default:
109 o = NULL;
110 }
111 }
113 const gchar* name = (const gchar*)sp_attribute_name(this->attr);
114 if(name && o) {
115 SP_OBJECT_REPR(o)->setAttribute((const gchar*) name, this->entry.get_text().c_str());
116 o->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
118 Glib::ustring undokey = "svgfonts:";
119 undokey += name;
120 SPDocumentUndo::maybe_done(o->document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS,
121 _("Set SVG Font attribute"));
122 }
124 }
126 Gtk::HBox* SvgFontsDialog::AttrCombo(gchar* lbl, const SPAttributeEnum /*attr*/){
127 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
128 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
129 hbox->add(* Gtk::manage(new Gtk::ComboBox()) );
130 hbox->show_all();
131 return hbox;
132 }
134 /*
135 Gtk::HBox* SvgFontsDialog::AttrSpin(gchar* lbl){
136 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
137 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
138 hbox->add(* Gtk::manage(new Gtk::SpinBox()) );
139 hbox->show_all();
140 return hbox;
141 }*/
143 /*** SvgFontsDialog ***/
145 GlyphComboBox::GlyphComboBox(){
146 }
148 void GlyphComboBox::update(SPFont* spfont){
149 if (!spfont) return
150 //TODO: figure out why do we need to append_text("") before clearing items properly...
152 this->append_text(""); //Gtk is refusing to clear the combobox when I comment out this line
153 this->clear_items();
155 for(SPObject* node = spfont->children; node; node=node->next){
156 if (SP_IS_GLYPH(node)){
157 this->append_text(((SPGlyph*)node)->unicode);
158 }
159 }
160 }
162 void SvgFontsDialog::on_kerning_value_changed(){
163 if (!this->kerning_pair) return;
164 SPDocument* document = sp_desktop_document(this->getDesktop());
166 //TODO: I am unsure whether this is the correct way of calling SPDocumentUndo::maybe_done
167 Glib::ustring undokey = "svgfonts:hkern:k:";
168 undokey += this->kerning_pair->u1->attribute_string();
169 undokey += ":";
170 undokey += this->kerning_pair->u2->attribute_string();
172 //slider values increase from right to left so that they match the kerning pair preview
174 //XML Tree being directly used here while it shouldn't be.
175 this->kerning_pair->getRepr()->setAttribute("k", Glib::Ascii::dtostr(get_selected_spfont()->horiz_adv_x - kerning_slider.get_value()).c_str());
176 SPDocumentUndo::maybe_done(document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS, _("Adjust kerning value"));
178 //populate_kerning_pairs_box();
179 kerning_preview.redraw();
180 _font_da.redraw();
181 }
183 void SvgFontsDialog::glyphs_list_button_release(GdkEventButton* event)
184 {
185 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
186 _GlyphsContextMenu.popup(event->button, event->time);
187 }
188 }
190 void SvgFontsDialog::kerning_pairs_list_button_release(GdkEventButton* event)
191 {
192 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
193 _KerningPairsContextMenu.popup(event->button, event->time);
194 }
195 }
197 void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event)
198 {
199 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
200 _FontsContextMenu.popup(event->button, event->time);
201 }
202 }
204 void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
205 {
206 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
207 _GlyphsContextMenu.append(*mi);
208 mi->signal_activate().connect(rem);
209 mi->show();
210 _GlyphsContextMenu.accelerate(parent);
211 }
213 void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
214 {
215 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
216 _KerningPairsContextMenu.append(*mi);
217 mi->signal_activate().connect(rem);
218 mi->show();
219 _KerningPairsContextMenu.accelerate(parent);
220 }
222 void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
223 {
224 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
225 _FontsContextMenu.append(*mi);
226 mi->signal_activate().connect(rem);
227 mi->show();
228 _FontsContextMenu.accelerate(parent);
229 }
231 void SvgFontsDialog::update_sensitiveness(){
232 if (get_selected_spfont()){
233 global_vbox.set_sensitive(true);
234 glyphs_vbox.set_sensitive(true);
235 kerning_vbox.set_sensitive(true);
236 } else {
237 global_vbox.set_sensitive(false);
238 glyphs_vbox.set_sensitive(false);
239 kerning_vbox.set_sensitive(false);
240 }
241 }
243 /* Add all fonts in the document to the combobox. */
244 void SvgFontsDialog::update_fonts()
245 {
246 SPDesktop* desktop = this->getDesktop();
247 SPDocument* document = sp_desktop_document(desktop);
248 const GSList* fonts = document->get_resource_list("font");
250 _model->clear();
251 for(const GSList *l = fonts; l; l = l->next) {
252 Gtk::TreeModel::Row row = *_model->append();
253 SPFont* f = (SPFont*)l->data;
254 row[_columns.spfont] = f;
255 row[_columns.svgfont] = new SvgFont(f);
256 const gchar* lbl = f->label();
257 const gchar* id = f->getId();
258 row[_columns.label] = lbl ? lbl : (id ? id : "font");
259 }
261 update_sensitiveness();
262 }
264 void SvgFontsDialog::on_preview_text_changed(){
265 _font_da.set_text((gchar*) _preview_entry.get_text().c_str());
266 _font_da.set_text(_preview_entry.get_text());
267 }
269 void SvgFontsDialog::on_kerning_pair_selection_changed(){
270 SPGlyphKerning* kern = get_selected_kerning_pair();
271 if (!kern) {
272 kerning_preview.set_text("");
273 return;
274 }
275 Glib::ustring str;
276 str += kern->u1->sample_glyph();
277 str += kern->u2->sample_glyph();
279 kerning_preview.set_text(str);
280 this->kerning_pair = kern;
282 //slider values increase from right to left so that they match the kerning pair preview
283 kerning_slider.set_value(get_selected_spfont()->horiz_adv_x - kern->k);
284 }
286 void SvgFontsDialog::update_global_settings_tab(){
287 SPFont* font = get_selected_spfont();
288 if (!font) return;
290 SPObject* obj;
291 for (obj=font->children; obj; obj=obj->next){
292 if (SP_IS_FONTFACE(obj)){
293 _familyname_entry->set_text(((SPFontFace*) obj)->font_family);
294 }
295 }
296 }
298 void SvgFontsDialog::on_font_selection_changed(){
299 SPFont* spfont = this->get_selected_spfont();
300 if (!spfont) return;
302 SvgFont* svgfont = this->get_selected_svgfont();
303 first_glyph.update(spfont);
304 second_glyph.update(spfont);
305 kerning_preview.set_svgfont(svgfont);
306 _font_da.set_svgfont(svgfont);
307 _font_da.redraw();
309 double set_width = spfont->horiz_adv_x;
310 setwidth_spin.set_value(set_width);
312 kerning_slider.set_range(0, set_width);
313 kerning_slider.set_draw_value(false);
314 kerning_slider.set_value(0);
316 update_global_settings_tab();
317 populate_glyphs_box();
318 populate_kerning_pairs_box();
319 update_sensitiveness();
320 }
322 void SvgFontsDialog::on_setwidth_changed(){
323 SPFont* spfont = this->get_selected_spfont();
324 if (spfont){
325 spfont->horiz_adv_x = setwidth_spin.get_value();
326 //TODO: tell cairo that the glyphs cache has to be invalidated
327 // The current solution is to recreate the whole cairo svgfont.
328 // This is not a good solution to the issue because big fonts will result in poor performance.
329 update_glyphs();
330 }
331 }
333 SPGlyphKerning* SvgFontsDialog::get_selected_kerning_pair()
334 {
335 Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
336 if(i)
337 return (*i)[_KerningPairsListColumns.spnode];
338 return NULL;
339 }
341 SvgFont* SvgFontsDialog::get_selected_svgfont()
342 {
343 Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
344 if(i)
345 return (*i)[_columns.svgfont];
346 return NULL;
347 }
349 SPFont* SvgFontsDialog::get_selected_spfont()
350 {
351 Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
352 if(i)
353 return (*i)[_columns.spfont];
354 return NULL;
355 }
357 SPGlyph* SvgFontsDialog::get_selected_glyph()
358 {
359 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
360 if(i)
361 return (*i)[_GlyphsListColumns.glyph_node];
362 return NULL;
363 }
365 Gtk::VBox* SvgFontsDialog::global_settings_tab(){
366 _familyname_entry = new AttrEntry(this, (gchar*) _("Family Name:"), SP_PROP_FONT_FAMILY);
368 global_vbox.pack_start(*_familyname_entry, false, false);
369 /* global_vbox->add(*AttrCombo((gchar*) _("Style:"), SP_PROP_FONT_STYLE));
370 global_vbox->add(*AttrCombo((gchar*) _("Variant:"), SP_PROP_FONT_VARIANT));
371 global_vbox->add(*AttrCombo((gchar*) _("Weight:"), SP_PROP_FONT_WEIGHT));
372 */
374 //Set Width (horiz_adv_x):
375 Gtk::HBox* setwidth_hbox = Gtk::manage(new Gtk::HBox());
376 setwidth_hbox->add(*Gtk::manage(new Gtk::Label(_("Set width:"))));
377 setwidth_hbox->add(setwidth_spin);
379 setwidth_spin.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_setwidth_changed));
380 setwidth_spin.set_range(0, 4096);
381 setwidth_spin.set_increments(10, 0);
382 global_vbox.pack_start(*setwidth_hbox, false, false);
384 return &global_vbox;
385 }
387 void
388 SvgFontsDialog::populate_glyphs_box()
389 {
390 if (!_GlyphsListStore) return;
391 _GlyphsListStore->clear();
393 SPFont* spfont = this->get_selected_spfont();
394 _glyphs_observer.set(spfont);
396 for(SPObject* node = spfont->children; node; node=node->next){
397 if (SP_IS_GLYPH(node)){
398 Gtk::TreeModel::Row row = *(_GlyphsListStore->append());
399 row[_GlyphsListColumns.glyph_node] = (SPGlyph*) node;
400 row[_GlyphsListColumns.glyph_name] = ((SPGlyph*) node)->glyph_name;
401 row[_GlyphsListColumns.unicode] = ((SPGlyph*) node)->unicode;
402 }
403 }
404 }
406 void
407 SvgFontsDialog::populate_kerning_pairs_box()
408 {
409 if (!_KerningPairsListStore) return;
410 _KerningPairsListStore->clear();
412 SPFont* spfont = this->get_selected_spfont();
414 for(SPObject* node = spfont->children; node; node=node->next){
415 if (SP_IS_HKERN(node)){
416 Gtk::TreeModel::Row row = *(_KerningPairsListStore->append());
417 row[_KerningPairsListColumns.first_glyph] = ((SPGlyphKerning*) node)->u1->attribute_string().c_str();
418 row[_KerningPairsListColumns.second_glyph] = ((SPGlyphKerning*) node)->u2->attribute_string().c_str();
419 row[_KerningPairsListColumns.kerning_value] = ((SPGlyphKerning*) node)->k;
420 row[_KerningPairsListColumns.spnode] = (SPGlyphKerning*) node;
421 }
422 }
423 }
425 SPGlyph *new_glyph(SPDocument* document, SPFont *font, const int count)
426 {
427 g_return_val_if_fail(font != NULL, NULL);
428 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
430 // create a new glyph
431 Inkscape::XML::Node *repr;
432 repr = xml_doc->createElement("svg:glyph");
434 std::ostringstream os;
435 os << _("glyph") << " " << count;
436 repr->setAttribute("glyph-name", os.str().c_str());
438 // Append the new glyph node to the current font
439 SP_OBJECT_REPR(font)->appendChild(repr);
440 Inkscape::GC::release(repr);
442 // get corresponding object
443 SPGlyph *g = SP_GLYPH( document->getObjectByRepr(repr) );
445 g_assert(g != NULL);
446 g_assert(SP_IS_GLYPH(g));
448 return g;
449 }
451 void SvgFontsDialog::update_glyphs(){
452 SPFont* font = get_selected_spfont();
453 if (!font) return;
454 populate_glyphs_box();
455 populate_kerning_pairs_box();
456 first_glyph.update(font);
457 second_glyph.update(font);
458 get_selected_svgfont()->refresh();
459 _font_da.redraw();
460 }
462 void SvgFontsDialog::add_glyph(){
463 const int count = _GlyphsListStore->children().size();
464 SPDocument* doc = sp_desktop_document(this->getDesktop());
465 /* SPGlyph* glyph =*/ new_glyph(doc, get_selected_spfont(), count+1);
467 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add glyph"));
469 update_glyphs();
470 }
472 void SvgFontsDialog::set_glyph_description_from_selected_path(){
473 SPDesktop* desktop = this->getDesktop();
474 if (!desktop) {
475 g_warning("SvgFontsDialog: No active desktop");
476 return;
477 }
479 Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
480 SPDocument* doc = sp_desktop_document(desktop);
481 Inkscape::Selection* sel = sp_desktop_selection(desktop);
482 if (sel->isEmpty()){
483 char *msg = _("Select a <b>path</b> to define the curves of a glyph");
484 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
485 return;
486 }
488 Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
489 if (!node) return;//TODO: should this be an assert?
490 if (!node->matchAttributeName("d") || !node->attribute("d")){
491 char *msg = _("The selected object does not have a <b>path</b> description.");
492 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
493 return;
494 } //TODO: //Is there a better way to tell it to to the user?
496 Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
498 //This matrix flips the glyph vertically
499 Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
500 pathv*=m;
501 //then we offset it
502 pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
504 SPGlyph* glyph = get_selected_glyph();
505 if (!glyph){
506 char *msg = _("No glyph selected in the SVGFonts dialog.");
507 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
508 return;
509 }
510 //XML Tree being directly used here while it shouldn't be.
511 glyph->getRepr()->setAttribute("d", (char*) sp_svg_write_path (pathv));
512 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
514 update_glyphs();
515 }
517 void SvgFontsDialog::missing_glyph_description_from_selected_path(){
518 SPDesktop* desktop = this->getDesktop();
519 if (!desktop) {
520 g_warning("SvgFontsDialog: No active desktop");
521 return;
522 }
524 Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
525 SPDocument* doc = sp_desktop_document(desktop);
526 Inkscape::Selection* sel = sp_desktop_selection(desktop);
527 if (sel->isEmpty()){
528 char *msg = _("Select a <b>path</b> to define the curves of a glyph");
529 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
530 return;
531 }
533 Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
534 if (!node) return;//TODO: should this be an assert?
535 if (!node->matchAttributeName("d") || !node->attribute("d")){
536 char *msg = _("The selected object does not have a <b>path</b> description.");
537 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
538 return;
539 } //TODO: //Is there a better way to tell it to to the user?
541 Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
543 //This matrix flips the glyph vertically
544 Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
545 pathv*=m;
546 //then we offset it
547 // pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
548 pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(1000));//TODO: use here the units-per-em attribute?
550 SPObject* obj;
551 for (obj = get_selected_spfont()->children; obj; obj=obj->next){
552 if (SP_IS_MISSING_GLYPH(obj)){
554 //XML Tree being directly used here while it shouldn't be.
555 obj->getRepr()->setAttribute("d", (char*) sp_svg_write_path (pathv));
556 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
557 }
558 }
560 update_glyphs();
561 }
563 void SvgFontsDialog::reset_missing_glyph_description(){
564 SPDesktop* desktop = this->getDesktop();
565 if (!desktop) {
566 g_warning("SvgFontsDialog: No active desktop");
567 return;
568 }
570 SPDocument* doc = sp_desktop_document(desktop);
571 SPObject* obj;
572 for (obj = get_selected_spfont()->children; obj; obj=obj->next){
573 if (SP_IS_MISSING_GLYPH(obj)){
574 //XML Tree being directly used here while it shouldn't be.
575 obj->getRepr()->setAttribute("d", (char*) "M0,0h1000v1024h-1000z");
576 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Reset missing-glyph"));
577 }
578 }
580 update_glyphs();
581 }
583 void SvgFontsDialog::glyph_name_edit(const Glib::ustring&, const Glib::ustring& str){
584 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
585 if (!i) return;
587 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
588 //XML Tree being directly used here while it shouldn't be.
589 glyph->getRepr()->setAttribute("glyph-name", str.c_str());
591 SPDocument* doc = sp_desktop_document(this->getDesktop());
592 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Edit glyph name"));
594 update_glyphs();
595 }
597 void SvgFontsDialog::glyph_unicode_edit(const Glib::ustring&, const Glib::ustring& str){
598 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
599 if (!i) return;
601 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
602 //XML Tree being directly used here while it shouldn't be.
603 glyph->getRepr()->setAttribute("unicode", str.c_str());
605 SPDocument* doc = sp_desktop_document(this->getDesktop());
606 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph unicode"));
608 update_glyphs();
609 }
611 void SvgFontsDialog::remove_selected_font(){
612 SPFont* font = get_selected_spfont();
614 //XML Tree being directly used here while it shouldn't be.
615 sp_repr_unparent(font->getRepr());
616 SPDocument* doc = sp_desktop_document(this->getDesktop());
617 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove font"));
619 update_fonts();
620 }
622 void SvgFontsDialog::remove_selected_glyph(){
623 if(!_GlyphsList.get_selection()) return;
625 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
626 if(!i) return;
628 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
630 //XML Tree being directly used here while it shouldn't be.
631 sp_repr_unparent(glyph->getRepr());
633 SPDocument* doc = sp_desktop_document(this->getDesktop());
634 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove glyph"));
636 update_glyphs();
637 }
639 void SvgFontsDialog::remove_selected_kerning_pair(){
640 if(!_KerningPairsList.get_selection()) return;
642 Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
643 if(!i) return;
645 SPGlyphKerning* pair = (*i)[_KerningPairsListColumns.spnode];
647 //XML Tree being directly used here while it shouldn't be.
648 sp_repr_unparent(pair->getRepr());
650 SPDocument* doc = sp_desktop_document(this->getDesktop());
651 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove kerning pair"));
653 update_glyphs();
654 }
656 Gtk::VBox* SvgFontsDialog::glyphs_tab(){
657 _GlyphsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::glyphs_list_button_release));
658 create_glyphs_popup_menu(_GlyphsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_glyph));
660 Gtk::HBox* missing_glyph_hbox = Gtk::manage(new Gtk::HBox());
661 Gtk::Label* missing_glyph_label = Gtk::manage(new Gtk::Label(_("Missing Glyph:")));
662 missing_glyph_hbox->pack_start(*missing_glyph_label, false,false);
663 missing_glyph_hbox->pack_start(missing_glyph_button, false,false);
664 missing_glyph_hbox->pack_start(missing_glyph_reset_button, false,false);
665 missing_glyph_button.set_label(_("From selection..."));
666 missing_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::missing_glyph_description_from_selected_path));
667 missing_glyph_reset_button.set_label(_("Reset"));
668 missing_glyph_reset_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::reset_missing_glyph_description));
670 glyphs_vbox.pack_start(*missing_glyph_hbox, false,false);
672 glyphs_vbox.add(_GlyphsListScroller);
673 _GlyphsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
674 _GlyphsListScroller.set_size_request(-1, 290);//It seems that is does not work. Why? I want a box with larger height
675 _GlyphsListScroller.add(_GlyphsList);
676 _GlyphsListStore = Gtk::ListStore::create(_GlyphsListColumns);
677 _GlyphsList.set_model(_GlyphsListStore);
678 _GlyphsList.append_column_editable(_("Glyph name"), _GlyphsListColumns.glyph_name);
679 _GlyphsList.append_column_editable(_("Matching string"), _GlyphsListColumns.unicode);
681 Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
682 add_glyph_button.set_label(_("Add Glyph"));
683 add_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_glyph));
685 hb->pack_start(add_glyph_button, false,false);
686 hb->pack_start(glyph_from_path_button, false,false);
688 glyphs_vbox.pack_start(*hb, false, false);
689 glyph_from_path_button.set_label(_("Get curves from selection..."));
690 glyph_from_path_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::set_glyph_description_from_selected_path));
692 dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(0))->signal_edited().connect(
693 sigc::mem_fun(*this, &SvgFontsDialog::glyph_name_edit));
695 dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(1))->signal_edited().connect(
696 sigc::mem_fun(*this, &SvgFontsDialog::glyph_unicode_edit));
698 _glyphs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_glyphs));
700 return &glyphs_vbox;
701 }
703 void SvgFontsDialog::add_kerning_pair(){
704 if (first_glyph.get_active_text() == "" ||
705 second_glyph.get_active_text() == "") return;
707 //look for this kerning pair on the currently selected font
708 this->kerning_pair = NULL;
709 for(SPObject* node = this->get_selected_spfont()->children; node; node=node->next){
710 //TODO: It is not really correct to get only the first byte of each string.
711 //TODO: We should also support vertical kerning
712 if (SP_IS_HKERN(node) && ((SPGlyphKerning*)node)->u1->contains((gchar) first_glyph.get_active_text().c_str()[0])
713 && ((SPGlyphKerning*)node)->u2->contains((gchar) second_glyph.get_active_text().c_str()[0]) ){
714 this->kerning_pair = (SPGlyphKerning*)node;
715 continue;
716 }
717 }
719 if (this->kerning_pair) return; //We already have this kerning pair
721 SPDocument* document = sp_desktop_document(this->getDesktop());
722 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
724 // create a new hkern node
725 Inkscape::XML::Node *repr;
726 repr = xml_doc->createElement("svg:hkern");
728 repr->setAttribute("u1", first_glyph.get_active_text().c_str());
729 repr->setAttribute("u2", second_glyph.get_active_text().c_str());
730 repr->setAttribute("k", "0");
732 // Append the new hkern node to the current font
733 SP_OBJECT_REPR(get_selected_spfont())->appendChild(repr);
734 Inkscape::GC::release(repr);
736 // get corresponding object
737 this->kerning_pair = SP_HKERN( document->getObjectByRepr(repr) );
739 SPDocumentUndo::done(document, SP_VERB_DIALOG_SVG_FONTS, _("Add kerning pair"));
740 }
742 Gtk::VBox* SvgFontsDialog::kerning_tab(){
743 _KerningPairsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::kerning_pairs_list_button_release));
744 create_kerning_pairs_popup_menu(_KerningPairsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_kerning_pair));
746 //Kerning Setup:
747 kerning_vbox.add(*Gtk::manage(new Gtk::Label(_("Kerning Setup:"))));
748 Gtk::HBox* kerning_selector = Gtk::manage(new Gtk::HBox());
749 kerning_selector->add(*Gtk::manage(new Gtk::Label(_("1st Glyph:"))));
750 kerning_selector->add(first_glyph);
751 kerning_selector->add(*Gtk::manage(new Gtk::Label(_("2nd Glyph:"))));
752 kerning_selector->add(second_glyph);
753 kerning_selector->add(add_kernpair_button);
754 add_kernpair_button.set_label(_("Add pair"));
755 add_kernpair_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_kerning_pair));
756 _KerningPairsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_pair_selection_changed));
757 kerning_slider.signal_value_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_value_changed));
759 kerning_vbox.pack_start(*kerning_selector, false,false);
761 kerning_vbox.add(_KerningPairsListScroller);
762 _KerningPairsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
763 _KerningPairsListScroller.add(_KerningPairsList);
764 _KerningPairsListStore = Gtk::ListStore::create(_KerningPairsListColumns);
765 _KerningPairsList.set_model(_KerningPairsListStore);
766 _KerningPairsList.append_column(_("First Unicode range"), _KerningPairsListColumns.first_glyph);
767 _KerningPairsList.append_column(_("Second Unicode range"), _KerningPairsListColumns.second_glyph);
768 // _KerningPairsList.append_column_numeric_editable(_("Kerning Value"), _KerningPairsListColumns.kerning_value, "%f");
770 kerning_vbox.add((Gtk::Widget&) kerning_preview);
772 Gtk::HBox* kerning_amount_hbox = Gtk::manage(new Gtk::HBox());
773 kerning_vbox.pack_start(*kerning_amount_hbox, false,false);
774 kerning_amount_hbox->add(*Gtk::manage(new Gtk::Label(_("Kerning value:"))));
775 kerning_amount_hbox->add(kerning_slider);
777 kerning_preview.set_size(300 + 20, 150 + 20);
778 _font_da.set_size(150 + 20, 50 + 20);
780 return &kerning_vbox;
781 }
783 SPFont *new_font(SPDocument *document)
784 {
785 g_return_val_if_fail(document != NULL, NULL);
787 SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
789 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
791 // create a new font
792 Inkscape::XML::Node *repr;
793 repr = xml_doc->createElement("svg:font");
795 //By default, set the horizontal advance to 1024 units
796 repr->setAttribute("horiz-adv-x", "1024");
798 // Append the new font node to defs
799 SP_OBJECT_REPR(defs)->appendChild(repr);
801 //create a missing glyph
802 Inkscape::XML::Node *fontface;
803 fontface = xml_doc->createElement("svg:font-face");
804 fontface->setAttribute("units-per-em", "1024");
805 repr->appendChild(fontface);
807 //create a missing glyph
808 Inkscape::XML::Node *mg;
809 mg = xml_doc->createElement("svg:missing-glyph");
810 mg->setAttribute("d", "M0,0h1000v1024h-1000z");
811 repr->appendChild(mg);
813 // get corresponding object
814 SPFont *f = SP_FONT( document->getObjectByRepr(repr) );
816 g_assert(f != NULL);
817 g_assert(SP_IS_FONT(f));
818 Inkscape::GC::release(mg);
819 Inkscape::GC::release(repr);
820 return f;
821 }
823 void set_font_family(SPFont* font, char* str){
824 if (!font) return;
825 SPObject* obj;
826 for (obj=font->children; obj; obj=obj->next){
827 if (SP_IS_FONTFACE(obj)){
828 //XML Tree being directly used here while it shouldn't be.
829 obj->getRepr()->setAttribute("font-family", str);
830 }
831 }
833 SPDocumentUndo::done(font->document, SP_VERB_DIALOG_SVG_FONTS, _("Set font family"));
834 }
836 void SvgFontsDialog::add_font(){
837 SPDocument* doc = sp_desktop_document(this->getDesktop());
838 SPFont* font = new_font(doc);
840 const int count = _model->children().size();
841 std::ostringstream os, os2;
842 os << _("font") << " " << count;
843 font->setLabel(os.str().c_str());
845 os2 << "SVGFont " << count;
846 SPObject* obj;
847 for (obj=font->children; obj; obj=obj->next){
848 if (SP_IS_FONTFACE(obj)){
849 //XML Tree being directly used here while it shouldn't be.
850 obj->getRepr()->setAttribute("font-family", os2.str().c_str());
851 }
852 }
854 update_fonts();
855 // select_font(font);
857 SPDocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add font"));
858 }
860 SvgFontsDialog::SvgFontsDialog()
861 : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), _add(Gtk::Stock::NEW)
862 {
863 _add.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_font));
865 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
866 Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
868 vbox->pack_start(_FontsList);
869 vbox->pack_start(_add, false, false);
870 hbox->add(*vbox);
871 hbox->add(_font_settings);
872 _getContents()->add(*hbox);
874 //List of SVGFonts declared in a document:
875 _model = Gtk::ListStore::create(_columns);
876 _FontsList.set_model(_model);
877 _FontsList.append_column_editable(_("_Font"), _columns.label);
878 _FontsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_font_selection_changed));
880 this->update_fonts();
882 Gtk::Notebook *tabs = Gtk::manage(new Gtk::Notebook());
883 tabs->set_scrollable();
885 tabs->append_page(*global_settings_tab(), _("_Global Settings"), true);
886 tabs->append_page(*glyphs_tab(), _("_Glyphs"), true);
887 tabs->append_page(*kerning_tab(), _("_Kerning"), true);
889 _font_settings.add(*tabs);
891 //Text Preview:
892 _preview_entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_preview_text_changed));
893 _getContents()->add((Gtk::Widget&) _font_da);
894 _preview_entry.set_text(_("Sample Text"));
895 _font_da.set_text(_("Sample Text"));
897 Gtk::HBox* preview_entry_hbox = Gtk::manage(new Gtk::HBox());
898 _getContents()->add(*preview_entry_hbox);
899 preview_entry_hbox->add(*Gtk::manage(new Gtk::Label(_("Preview Text:"))));
900 preview_entry_hbox->add(_preview_entry);
902 _FontsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::fonts_list_button_release));
903 create_fonts_popup_menu(_FontsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_font));
905 _defs_observer.set(SP_DOCUMENT_DEFS(sp_desktop_document(this->getDesktop())));
906 _defs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_fonts));
908 _getContents()->show_all();
909 }
911 SvgFontsDialog::~SvgFontsDialog(){}
913 } // namespace Dialog
914 } // namespace UI
915 } // namespace Inkscape
917 #endif //#ifdef ENABLE_SVG_FONTS
919 /*
920 Local Variables:
921 mode:c++
922 c-file-style:"stroustrup"
923 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
924 indent-tabs-mode:nil
925 fill-column:99
926 End:
927 */
928 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :