Code

Merge from trunk
[inkscape.git] / src / ui / dialog / svg-fonts-dialog.cpp
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         sp_document_maybe_done(o->document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS,
121                                _("Set SVG Font attribute"));
122     }
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;
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(){
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     }
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 sp_document_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
173     this->kerning_pair->repr->setAttribute("k", Glib::Ascii::dtostr(get_selected_spfont()->horiz_adv_x - kerning_slider.get_value()).c_str());
174     sp_document_maybe_done(document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS, _("Adjust kerning value"));
176     //populate_kerning_pairs_box();
177     kerning_preview.redraw();
178     _font_da.redraw();
181 void SvgFontsDialog::glyphs_list_button_release(GdkEventButton* event)
183     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
184         _GlyphsContextMenu.popup(event->button, event->time);
185     }
188 void SvgFontsDialog::kerning_pairs_list_button_release(GdkEventButton* event)
190     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
191         _KerningPairsContextMenu.popup(event->button, event->time);
192     }
195 void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event)
197     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
198         _FontsContextMenu.popup(event->button, event->time);
199     }
202 void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
204     Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
205     _GlyphsContextMenu.append(*mi);
206     mi->signal_activate().connect(rem);
207     mi->show();
208     _GlyphsContextMenu.accelerate(parent);
211 void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
213     Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
214     _KerningPairsContextMenu.append(*mi);
215     mi->signal_activate().connect(rem);
216     mi->show();
217     _KerningPairsContextMenu.accelerate(parent);
220 void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
222     Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
223     _FontsContextMenu.append(*mi);
224     mi->signal_activate().connect(rem);
225     mi->show();
226     _FontsContextMenu.accelerate(parent);
229 void SvgFontsDialog::update_sensitiveness(){
230     if (get_selected_spfont()){
231         global_vbox.set_sensitive(true);
232         glyphs_vbox.set_sensitive(true);
233         kerning_vbox.set_sensitive(true);
234     } else {
235         global_vbox.set_sensitive(false);
236         glyphs_vbox.set_sensitive(false);
237         kerning_vbox.set_sensitive(false);
238     }
241 /* Add all fonts in the document to the combobox. */
242 void SvgFontsDialog::update_fonts()
244     SPDesktop* desktop = this->getDesktop();
245     SPDocument* document = sp_desktop_document(desktop);
246     const GSList* fonts = sp_document_get_resource_list(document, "font");
248     _model->clear();
249     for(const GSList *l = fonts; l; l = l->next) {
250         Gtk::TreeModel::Row row = *_model->append();
251         SPFont* f = (SPFont*)l->data;
252         row[_columns.spfont] = f;
253         row[_columns.svgfont] = new SvgFont(f);
254         const gchar* lbl = f->label();
255         const gchar* id = f->getId();
256         row[_columns.label] = lbl ? lbl : (id ? id : "font");
257     }
259     update_sensitiveness();
262 void SvgFontsDialog::on_preview_text_changed(){
263     _font_da.set_text((gchar*) _preview_entry.get_text().c_str());
264     _font_da.set_text(_preview_entry.get_text());
267 void SvgFontsDialog::on_kerning_pair_selection_changed(){
268     SPGlyphKerning* kern = get_selected_kerning_pair();
269     if (!kern) {
270         kerning_preview.set_text("");
271         return;
272     }
273     Glib::ustring str;
274     str += kern->u1->sample_glyph();
275     str += kern->u2->sample_glyph();
277     kerning_preview.set_text(str);
278     this->kerning_pair = kern;
280     //slider values increase from right to left so that they match the kerning pair preview
281     kerning_slider.set_value(get_selected_spfont()->horiz_adv_x - kern->k);
284 void SvgFontsDialog::update_global_settings_tab(){
285     SPFont* font = get_selected_spfont();
286     if (!font) return;
288     SPObject* obj;
289     for (obj=font->children; obj; obj=obj->next){
290         if (SP_IS_FONTFACE(obj)){
291             _familyname_entry->set_text(((SPFontFace*) obj)->font_family);
292         }
293     }
296 void SvgFontsDialog::on_font_selection_changed(){
297     SPFont* spfont = this->get_selected_spfont();
298     if (!spfont) return;
300     SvgFont* svgfont = this->get_selected_svgfont();
301     first_glyph.update(spfont);
302     second_glyph.update(spfont);
303     kerning_preview.set_svgfont(svgfont);
304     _font_da.set_svgfont(svgfont);
305     _font_da.redraw();
307     double set_width = spfont->horiz_adv_x;
308     setwidth_spin.set_value(set_width);
310     kerning_slider.set_range(0, set_width);
311     kerning_slider.set_draw_value(false);
312     kerning_slider.set_value(0);
314     update_global_settings_tab();
315     populate_glyphs_box();
316     populate_kerning_pairs_box();
317     update_sensitiveness();
320 void SvgFontsDialog::on_setwidth_changed(){
321     SPFont* spfont = this->get_selected_spfont();
322     if (spfont){
323         spfont->horiz_adv_x = setwidth_spin.get_value();
324         //TODO: tell cairo that the glyphs cache has to be invalidated
325         //    The current solution is to recreate the whole cairo svgfont.
326         //    This is not a good solution to the issue because big fonts will result in poor performance.
327         update_glyphs();
328     }
331 SPGlyphKerning* SvgFontsDialog::get_selected_kerning_pair()
333     Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
334     if(i)
335         return (*i)[_KerningPairsListColumns.spnode];
336     return NULL;
339 SvgFont* SvgFontsDialog::get_selected_svgfont()
341     Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
342     if(i)
343         return (*i)[_columns.svgfont];
344     return NULL;
347 SPFont* SvgFontsDialog::get_selected_spfont()
349     Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
350     if(i)
351         return (*i)[_columns.spfont];
352     return NULL;
355 SPGlyph* SvgFontsDialog::get_selected_glyph()
357     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
358     if(i)
359         return (*i)[_GlyphsListColumns.glyph_node];
360     return NULL;
363 Gtk::VBox* SvgFontsDialog::global_settings_tab(){
364     _familyname_entry = new AttrEntry(this, (gchar*) _("Family Name:"), SP_PROP_FONT_FAMILY);
366     global_vbox.pack_start(*_familyname_entry, false, false);
367 /*    global_vbox->add(*AttrCombo((gchar*) _("Style:"), SP_PROP_FONT_STYLE));
368     global_vbox->add(*AttrCombo((gchar*) _("Variant:"), SP_PROP_FONT_VARIANT));
369     global_vbox->add(*AttrCombo((gchar*) _("Weight:"), SP_PROP_FONT_WEIGHT));
370 */
372 //Set Width (horiz_adv_x):
373     Gtk::HBox* setwidth_hbox = Gtk::manage(new Gtk::HBox());
374     setwidth_hbox->add(*Gtk::manage(new Gtk::Label(_("Set width:"))));
375     setwidth_hbox->add(setwidth_spin);
377     setwidth_spin.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_setwidth_changed));
378     setwidth_spin.set_range(0, 4096);
379     setwidth_spin.set_increments(10, 0);
380     global_vbox.pack_start(*setwidth_hbox, false, false);
382     return &global_vbox;
385 void
386 SvgFontsDialog::populate_glyphs_box()
388     if (!_GlyphsListStore) return;
389     _GlyphsListStore->clear();
391     SPFont* spfont = this->get_selected_spfont();
392     _glyphs_observer.set(spfont);
394     for(SPObject* node = spfont->children; node; node=node->next){
395         if (SP_IS_GLYPH(node)){
396             Gtk::TreeModel::Row row = *(_GlyphsListStore->append());
397             row[_GlyphsListColumns.glyph_node] = (SPGlyph*) node;
398             row[_GlyphsListColumns.glyph_name] = ((SPGlyph*) node)->glyph_name;
399             row[_GlyphsListColumns.unicode] = ((SPGlyph*) node)->unicode;
400         }
401     }
404 void
405 SvgFontsDialog::populate_kerning_pairs_box()
407     if (!_KerningPairsListStore) return;
408     _KerningPairsListStore->clear();
410     SPFont* spfont = this->get_selected_spfont();
412     for(SPObject* node = spfont->children; node; node=node->next){
413         if (SP_IS_HKERN(node)){
414             Gtk::TreeModel::Row row = *(_KerningPairsListStore->append());
415             row[_KerningPairsListColumns.first_glyph] = ((SPGlyphKerning*) node)->u1->attribute_string().c_str();
416             row[_KerningPairsListColumns.second_glyph] = ((SPGlyphKerning*) node)->u2->attribute_string().c_str();
417             row[_KerningPairsListColumns.kerning_value] = ((SPGlyphKerning*) node)->k;
418             row[_KerningPairsListColumns.spnode] = (SPGlyphKerning*) node;
419         }
420     }
423 SPGlyph *new_glyph(SPDocument* document, SPFont *font, const int count)
425     g_return_val_if_fail(font != NULL, NULL);
426     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
428     // create a new glyph
429     Inkscape::XML::Node *repr;
430     repr = xml_doc->createElement("svg:glyph");
432     std::ostringstream os;
433     os << _("glyph") << " " << count;
434     repr->setAttribute("glyph-name", os.str().c_str());
436     // Append the new glyph node to the current font
437     SP_OBJECT_REPR(font)->appendChild(repr);
438     Inkscape::GC::release(repr);
440     // get corresponding object
441     SPGlyph *g = SP_GLYPH( document->getObjectByRepr(repr) );
443     g_assert(g != NULL);
444     g_assert(SP_IS_GLYPH(g));
446     return g;
449 void SvgFontsDialog::update_glyphs(){
450     SPFont* font = get_selected_spfont();
451     if (!font) return;
452     populate_glyphs_box();
453     populate_kerning_pairs_box();
454     first_glyph.update(font);
455     second_glyph.update(font);
456     get_selected_svgfont()->refresh();
457     _font_da.redraw();
460 void SvgFontsDialog::add_glyph(){
461     const int count = _GlyphsListStore->children().size();
462     SPDocument* doc = sp_desktop_document(this->getDesktop());
463     /* SPGlyph* glyph =*/ new_glyph(doc, get_selected_spfont(), count+1);
465     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add glyph"));
467     update_glyphs();
470 void SvgFontsDialog::set_glyph_description_from_selected_path(){
471     SPDesktop* desktop = this->getDesktop();
472     if (!desktop) {
473         g_warning("SvgFontsDialog: No active desktop");
474         return;
475     }
477     Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
478     SPDocument* doc = sp_desktop_document(desktop);
479     Inkscape::Selection* sel = sp_desktop_selection(desktop);
480     if (sel->isEmpty()){
481         char *msg = _("Select a <b>path</b> to define the curves of a glyph");
482         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
483         return;
484     }
486     Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
487     if (!node) return;//TODO: should this be an assert?
488     if (!node->matchAttributeName("d") || !node->attribute("d")){
489         char *msg = _("The selected object does not have a <b>path</b> description.");
490         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
491         return;
492     } //TODO: //Is there a better way to tell it to to the user?
494     Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
496     //This matrix flips the glyph vertically
497     Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
498     pathv*=m;
499     //then we offset it
500     pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
502     SPGlyph* glyph = get_selected_glyph();
503     if (!glyph){
504         char *msg = _("No glyph selected in the SVGFonts dialog.");
505         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
506         return;
507     }
508     glyph->repr->setAttribute("d", (char*) sp_svg_write_path (pathv));
509     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
511     update_glyphs();
514 void SvgFontsDialog::missing_glyph_description_from_selected_path(){
515     SPDesktop* desktop = this->getDesktop();
516     if (!desktop) {
517         g_warning("SvgFontsDialog: No active desktop");
518         return;
519     }
521     Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
522     SPDocument* doc = sp_desktop_document(desktop);
523     Inkscape::Selection* sel = sp_desktop_selection(desktop);
524     if (sel->isEmpty()){
525         char *msg = _("Select a <b>path</b> to define the curves of a glyph");
526         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
527         return;
528     }
530     Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
531     if (!node) return;//TODO: should this be an assert?
532     if (!node->matchAttributeName("d") || !node->attribute("d")){
533         char *msg = _("The selected object does not have a <b>path</b> description.");
534         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
535         return;
536     } //TODO: //Is there a better way to tell it to to the user?
538     Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
540     //This matrix flips the glyph vertically
541     Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
542     pathv*=m;
543     //then we offset it
544 //  pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
545     pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(1000));//TODO: use here the units-per-em attribute?
547     SPObject* obj;
548     for (obj = get_selected_spfont()->children; obj; obj=obj->next){
549         if (SP_IS_MISSING_GLYPH(obj)){
550             obj->repr->setAttribute("d", (char*) sp_svg_write_path (pathv));
551             sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
552         }
553     }
555     update_glyphs();
558 void SvgFontsDialog::reset_missing_glyph_description(){
559     SPDesktop* desktop = this->getDesktop();
560     if (!desktop) {
561         g_warning("SvgFontsDialog: No active desktop");
562         return;
563     }
565     SPDocument* doc = sp_desktop_document(desktop);
566     SPObject* obj;
567     for (obj = get_selected_spfont()->children; obj; obj=obj->next){
568         if (SP_IS_MISSING_GLYPH(obj)){
569             obj->repr->setAttribute("d", (char*) "M0,0h1000v1024h-1000z");
570             sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Reset missing-glyph"));
571         }
572     }
574     update_glyphs();
577 void SvgFontsDialog::glyph_name_edit(const Glib::ustring&, const Glib::ustring& str){
578     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
579     if (!i) return;
581     SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
582     glyph->repr->setAttribute("glyph-name", str.c_str());
584     SPDocument* doc = sp_desktop_document(this->getDesktop());
585     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Edit glyph name"));
587     update_glyphs();
590 void SvgFontsDialog::glyph_unicode_edit(const Glib::ustring&, const Glib::ustring& str){
591     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
592     if (!i) return;
594     SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
595     glyph->repr->setAttribute("unicode", str.c_str());
597     SPDocument* doc = sp_desktop_document(this->getDesktop());
598     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph unicode"));
600     update_glyphs();
603 void SvgFontsDialog::remove_selected_font(){
604     SPFont* font = get_selected_spfont();
606     sp_repr_unparent(font->repr);
607     SPDocument* doc = sp_desktop_document(this->getDesktop());
608     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove font"));
610     update_fonts();
613 void SvgFontsDialog::remove_selected_glyph(){
614     if(!_GlyphsList.get_selection()) return;
616     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
617     if(!i) return;
619     SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
620     sp_repr_unparent(glyph->repr);
622     SPDocument* doc = sp_desktop_document(this->getDesktop());
623     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove glyph"));
625     update_glyphs();
628 void SvgFontsDialog::remove_selected_kerning_pair(){
629     if(!_KerningPairsList.get_selection()) return;
631     Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
632     if(!i) return;
634     SPGlyphKerning* pair = (*i)[_KerningPairsListColumns.spnode];
635     sp_repr_unparent(pair->repr);
637     SPDocument* doc = sp_desktop_document(this->getDesktop());
638     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove kerning pair"));
640     update_glyphs();
643 Gtk::VBox* SvgFontsDialog::glyphs_tab(){
644     _GlyphsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::glyphs_list_button_release));
645     create_glyphs_popup_menu(_GlyphsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_glyph));
647     Gtk::HBox* missing_glyph_hbox = Gtk::manage(new Gtk::HBox());
648     Gtk::Label* missing_glyph_label = Gtk::manage(new Gtk::Label(_("Missing Glyph:")));
649     missing_glyph_hbox->pack_start(*missing_glyph_label, false,false);
650     missing_glyph_hbox->pack_start(missing_glyph_button, false,false);
651     missing_glyph_hbox->pack_start(missing_glyph_reset_button, false,false);
652     missing_glyph_button.set_label(_("From selection..."));
653     missing_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::missing_glyph_description_from_selected_path));
654     missing_glyph_reset_button.set_label(_("Reset"));
655     missing_glyph_reset_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::reset_missing_glyph_description));
657     glyphs_vbox.pack_start(*missing_glyph_hbox, false,false);
659     glyphs_vbox.add(_GlyphsListScroller);
660     _GlyphsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
661     _GlyphsListScroller.set_size_request(-1, 290);//It seems that is does not work. Why? I want a box with larger height
662     _GlyphsListScroller.add(_GlyphsList);
663     _GlyphsListStore = Gtk::ListStore::create(_GlyphsListColumns);
664     _GlyphsList.set_model(_GlyphsListStore);
665     _GlyphsList.append_column_editable(_("Glyph name"), _GlyphsListColumns.glyph_name);
666     _GlyphsList.append_column_editable(_("Matching string"), _GlyphsListColumns.unicode);
668     Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
669     add_glyph_button.set_label(_("Add Glyph"));
670     add_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_glyph));
672     hb->pack_start(add_glyph_button, false,false);
673     hb->pack_start(glyph_from_path_button, false,false);
675     glyphs_vbox.pack_start(*hb, false, false);
676     glyph_from_path_button.set_label(_("Get curves from selection..."));
677     glyph_from_path_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::set_glyph_description_from_selected_path));
679     dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(0))->signal_edited().connect(
680         sigc::mem_fun(*this, &SvgFontsDialog::glyph_name_edit));
682     dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(1))->signal_edited().connect(
683         sigc::mem_fun(*this, &SvgFontsDialog::glyph_unicode_edit));
685     _glyphs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_glyphs));
687     return &glyphs_vbox;
690 void SvgFontsDialog::add_kerning_pair(){
691     if (first_glyph.get_active_text() == "" ||
692         second_glyph.get_active_text() == "") return;
694     //look for this kerning pair on the currently selected font
695     this->kerning_pair = NULL;
696     for(SPObject* node = this->get_selected_spfont()->children; node; node=node->next){
697         //TODO: It is not really correct to get only the first byte of each string.
698         //TODO: We should also support vertical kerning
699         if (SP_IS_HKERN(node) && ((SPGlyphKerning*)node)->u1->contains((gchar) first_glyph.get_active_text().c_str()[0])
700             && ((SPGlyphKerning*)node)->u2->contains((gchar) second_glyph.get_active_text().c_str()[0]) ){
701             this->kerning_pair = (SPGlyphKerning*)node;
702             continue;
703         }
704     }
706     if (this->kerning_pair) return; //We already have this kerning pair
708     SPDocument* document = sp_desktop_document(this->getDesktop());
709     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
711     // create a new hkern node
712     Inkscape::XML::Node *repr;
713     repr = xml_doc->createElement("svg:hkern");
715     repr->setAttribute("u1", first_glyph.get_active_text().c_str());
716     repr->setAttribute("u2", second_glyph.get_active_text().c_str());
717     repr->setAttribute("k", "0");
719     // Append the new hkern node to the current font
720     SP_OBJECT_REPR(get_selected_spfont())->appendChild(repr);
721     Inkscape::GC::release(repr);
723     // get corresponding object
724     this->kerning_pair = SP_HKERN( document->getObjectByRepr(repr) );
726     sp_document_done(document, SP_VERB_DIALOG_SVG_FONTS, _("Add kerning pair"));
729 Gtk::VBox* SvgFontsDialog::kerning_tab(){
730     _KerningPairsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::kerning_pairs_list_button_release));
731     create_kerning_pairs_popup_menu(_KerningPairsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_kerning_pair));
733 //Kerning Setup:
734     kerning_vbox.add(*Gtk::manage(new Gtk::Label(_("Kerning Setup:"))));
735     Gtk::HBox* kerning_selector = Gtk::manage(new Gtk::HBox());
736     kerning_selector->add(*Gtk::manage(new Gtk::Label(_("1st Glyph:"))));
737     kerning_selector->add(first_glyph);
738     kerning_selector->add(*Gtk::manage(new Gtk::Label(_("2nd Glyph:"))));
739     kerning_selector->add(second_glyph);
740     kerning_selector->add(add_kernpair_button);
741     add_kernpair_button.set_label(_("Add pair"));
742     add_kernpair_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_kerning_pair));
743     _KerningPairsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_pair_selection_changed));
744     kerning_slider.signal_value_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_value_changed));
746     kerning_vbox.pack_start(*kerning_selector, false,false);
748     kerning_vbox.add(_KerningPairsListScroller);
749     _KerningPairsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
750     _KerningPairsListScroller.add(_KerningPairsList);
751     _KerningPairsListStore = Gtk::ListStore::create(_KerningPairsListColumns);
752     _KerningPairsList.set_model(_KerningPairsListStore);
753     _KerningPairsList.append_column(_("First Unicode range"), _KerningPairsListColumns.first_glyph);
754     _KerningPairsList.append_column(_("Second Unicode range"), _KerningPairsListColumns.second_glyph);
755 //    _KerningPairsList.append_column_numeric_editable(_("Kerning Value"), _KerningPairsListColumns.kerning_value, "%f");
757     kerning_vbox.add((Gtk::Widget&) kerning_preview);
759     Gtk::HBox* kerning_amount_hbox = Gtk::manage(new Gtk::HBox());
760     kerning_vbox.pack_start(*kerning_amount_hbox, false,false);
761     kerning_amount_hbox->add(*Gtk::manage(new Gtk::Label(_("Kerning value:"))));
762     kerning_amount_hbox->add(kerning_slider);
764     kerning_preview.set_size(300 + 20, 150 + 20);
765     _font_da.set_size(150 + 20, 50 + 20);
767     return &kerning_vbox;
770 SPFont *new_font(SPDocument *document)
772     g_return_val_if_fail(document != NULL, NULL);
774     SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
776     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
778     // create a new font
779     Inkscape::XML::Node *repr;
780     repr = xml_doc->createElement("svg:font");
782     //By default, set the horizontal advance to 1024 units
783     repr->setAttribute("horiz-adv-x", "1024");
785     // Append the new font node to defs
786     SP_OBJECT_REPR(defs)->appendChild(repr);
788     //create a missing glyph
789     Inkscape::XML::Node *fontface;
790     fontface = xml_doc->createElement("svg:font-face");
791     fontface->setAttribute("units-per-em", "1024");
792     repr->appendChild(fontface);
794     //create a missing glyph
795     Inkscape::XML::Node *mg;
796     mg = xml_doc->createElement("svg:missing-glyph");
797     mg->setAttribute("d", "M0,0h1000v1024h-1000z");
798     repr->appendChild(mg);
800     // get corresponding object
801     SPFont *f = SP_FONT( document->getObjectByRepr(repr) );
803     g_assert(f != NULL);
804     g_assert(SP_IS_FONT(f));
805     Inkscape::GC::release(mg);
806     Inkscape::GC::release(repr);
807     return f;
810 void set_font_family(SPFont* font, char* str){
811     if (!font) return;
812     SPObject* obj;
813     for (obj=font->children; obj; obj=obj->next){
814         if (SP_IS_FONTFACE(obj)){
815             obj->repr->setAttribute("font-family", str);
816         }
817     }
819     sp_document_done(font->document, SP_VERB_DIALOG_SVG_FONTS, _("Set font family"));
822 void SvgFontsDialog::add_font(){
823     SPDocument* doc = sp_desktop_document(this->getDesktop());
824     SPFont* font = new_font(doc);
826     const int count = _model->children().size();
827     std::ostringstream os, os2;
828     os << _("font") << " " << count;
829     font->setLabel(os.str().c_str());
831     os2 << "SVGFont " << count;
832     SPObject* obj;
833     for (obj=font->children; obj; obj=obj->next){
834         if (SP_IS_FONTFACE(obj)){
835             obj->repr->setAttribute("font-family", os2.str().c_str());
836         }
837     }
839     update_fonts();
840 //    select_font(font);
842     sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add font"));
845 SvgFontsDialog::SvgFontsDialog()
846  : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), _add(Gtk::Stock::NEW)
848     _add.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_font));
850     Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
851     Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
853     vbox->pack_start(_FontsList);
854     vbox->pack_start(_add, false, false);
855     hbox->add(*vbox);
856     hbox->add(_font_settings);
857     _getContents()->add(*hbox);
859 //List of SVGFonts declared in a document:
860     _model = Gtk::ListStore::create(_columns);
861     _FontsList.set_model(_model);
862     _FontsList.append_column_editable(_("_Font"), _columns.label);
863     _FontsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_font_selection_changed));
865     this->update_fonts();
867     Gtk::Notebook *tabs = Gtk::manage(new Gtk::Notebook());
868     tabs->set_scrollable();
870     tabs->append_page(*global_settings_tab(), _("_Global Settings"), true);
871     tabs->append_page(*glyphs_tab(), _("_Glyphs"), true);
872     tabs->append_page(*kerning_tab(), _("_Kerning"), true);
874     _font_settings.add(*tabs);
876 //Text Preview:
877     _preview_entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_preview_text_changed));
878     _getContents()->add((Gtk::Widget&) _font_da);
879     _preview_entry.set_text(_("Sample Text"));
880     _font_da.set_text(_("Sample Text"));
882     Gtk::HBox* preview_entry_hbox = Gtk::manage(new Gtk::HBox());
883     _getContents()->add(*preview_entry_hbox);
884     preview_entry_hbox->add(*Gtk::manage(new Gtk::Label(_("Preview Text:"))));
885     preview_entry_hbox->add(_preview_entry);
887     _FontsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::fonts_list_button_release));
888     create_fonts_popup_menu(_FontsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_font));
890     _defs_observer.set(SP_DOCUMENT_DEFS(sp_desktop_document(this->getDesktop())));
891     _defs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_fonts));
893     _getContents()->show_all();
896 SvgFontsDialog::~SvgFontsDialog(){}
898 } // namespace Dialog
899 } // namespace UI
900 } // namespace Inkscape
902 #endif //#ifdef ENABLE_SVG_FONTS
904 /*
905   Local Variables:
906   mode:c++
907   c-file-style:"stroustrup"
908   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
909   indent-tabs-mode:nil
910   fill-column:99
911   End:
912 */
913 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :