Code

Merge and cleanup of GSoC C++-ification project.
[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  *   Jon A. Cruz <jon@joncruz.org>
7  *   Abhishek Sharma
8  *
9  * Copyright (C) 2008 Authors
10  * Released under GNU GPLv2 (or later).  Read the file 'COPYING' for more information.
11  */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #ifdef ENABLE_SVG_FONTS
19 #include <2geom/pathvector.h>
20 #include "document-private.h"
21 #include <gtkmm/notebook.h>
22 #include <glibmm/i18n.h>
23 #include <message-stack.h>
24 #include "selection.h"
25 #include <string.h>
26 #include "svg/svg.h"
27 #include "svg-fonts-dialog.h"
28 #include "xml/node.h"
29 #include "xml/repr.h"
31 SvgFontDrawingArea::SvgFontDrawingArea(){
32     this->text = "";
33     this->svgfont = NULL;
34 }
36 void SvgFontDrawingArea::set_svgfont(SvgFont* svgfont){
37     this->svgfont = svgfont;
38 }
40 void SvgFontDrawingArea::set_text(Glib::ustring text){
41     this->text = text;
42     redraw();
43 }
45 void SvgFontDrawingArea::set_size(int x, int y){
46     this->x = x;
47     this->y = y;
48     ((Gtk::Widget*) this)->set_size_request(x, y);
49 }
51 void SvgFontDrawingArea::redraw(){
52     ((Gtk::Widget*) this)->queue_draw();
53 }
55 bool SvgFontDrawingArea::on_expose_event (GdkEventExpose */*event*/){
56   if (this->svgfont){
57     Glib::RefPtr<Gdk::Window> window = get_window();
58     Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
59     cr->set_font_face( Cairo::RefPtr<Cairo::FontFace>(new Cairo::FontFace(this->svgfont->get_font_face(), false /* does not have reference */)) );
60     cr->set_font_size (this->y-20);
61     cr->move_to (10, 10);
62     cr->show_text (this->text.c_str());
63   }
64   return TRUE;
65 }
67 namespace Inkscape {
68 namespace UI {
69 namespace Dialog {
71 /*
72 Gtk::HBox* SvgFontsDialog::AttrEntry(gchar* lbl, const SPAttributeEnum attr){
73     Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
74     hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
75     Gtk::Entry* entry = Gtk::manage(new Gtk::Entry());
76     hbox->add(* entry );
77     hbox->show_all();
79     entry->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_attr_changed));
80     return hbox;
81 }
82 */
84 SvgFontsDialog::AttrEntry::AttrEntry(SvgFontsDialog* d, gchar* lbl, const SPAttributeEnum attr){
85     this->dialog = d;
86     this->attr = attr;
87     this->add(* Gtk::manage(new Gtk::Label(lbl)) );
88     this->add(entry);
89     this->show_all();
91     entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::AttrEntry::on_attr_changed));
92 }
94 void SvgFontsDialog::AttrEntry::set_text(char* t){
95     if (!t) return;
96     entry.set_text(t);
97 }
99 void SvgFontsDialog::AttrEntry::on_attr_changed(){
101     SPObject* o = NULL;
102     for(SPObject* node = this->dialog->get_selected_spfont()->children; node; node=node->next){
103         switch(this->attr){
104             case SP_PROP_FONT_FAMILY:
105                 if (SP_IS_FONTFACE(node)){
106                     o = node;
107                     continue;
108                 }
109                 break;
110             default:
111                 o = NULL;
112         }
113     }
115     const gchar* name = (const gchar*)sp_attribute_name(this->attr);
116     if(name && o) {
117         SP_OBJECT_REPR(o)->setAttribute((const gchar*) name, this->entry.get_text().c_str());
118         o->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
120         Glib::ustring undokey = "svgfonts:";
121         undokey += name;
122         DocumentUndo::maybeDone(o->document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS,
123                                 _("Set SVG Font attribute"));
124     }
128 Gtk::HBox* SvgFontsDialog::AttrCombo(gchar* lbl, const SPAttributeEnum /*attr*/){
129     Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
130     hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
131     hbox->add(* Gtk::manage(new Gtk::ComboBox()) );
132     hbox->show_all();
133     return hbox;
136 /*
137 Gtk::HBox* SvgFontsDialog::AttrSpin(gchar* lbl){
138     Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
139     hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
140     hbox->add(* Gtk::manage(new Gtk::SpinBox()) );
141     hbox->show_all();
142     return hbox;
143 }*/
145 /*** SvgFontsDialog ***/
147 GlyphComboBox::GlyphComboBox(){
150 void GlyphComboBox::update(SPFont* spfont){
151     if (!spfont) return
152 //TODO: figure out why do we need to append_text("") before clearing items properly...
154     this->append_text(""); //Gtk is refusing to clear the combobox when I comment out this line
155     this->clear_items();
157     for(SPObject* node = spfont->children; node; node=node->next){
158         if (SP_IS_GLYPH(node)){
159             this->append_text(((SPGlyph*)node)->unicode);
160         }
161     }
164 void SvgFontsDialog::on_kerning_value_changed(){
165     if (!this->kerning_pair) return;
166     SPDocument* document = sp_desktop_document(this->getDesktop());
168     //TODO: I am unsure whether this is the correct way of calling SPDocumentUndo::maybe_done
169     Glib::ustring undokey = "svgfonts:hkern:k:";
170     undokey += this->kerning_pair->u1->attribute_string();
171     undokey += ":";
172     undokey += this->kerning_pair->u2->attribute_string();
174     //slider values increase from right to left so that they match the kerning pair preview
176     //XML Tree being directly used here while it shouldn't be.
177     this->kerning_pair->getRepr()->setAttribute("k", Glib::Ascii::dtostr(get_selected_spfont()->horiz_adv_x - kerning_slider.get_value()).c_str());
178     DocumentUndo::maybeDone(document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS, _("Adjust kerning value"));
180     //populate_kerning_pairs_box();
181     kerning_preview.redraw();
182     _font_da.redraw();
185 void SvgFontsDialog::glyphs_list_button_release(GdkEventButton* event)
187     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
188         _GlyphsContextMenu.popup(event->button, event->time);
189     }
192 void SvgFontsDialog::kerning_pairs_list_button_release(GdkEventButton* event)
194     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
195         _KerningPairsContextMenu.popup(event->button, event->time);
196     }
199 void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event)
201     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
202         _FontsContextMenu.popup(event->button, event->time);
203     }
206 void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
208     Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
209     _GlyphsContextMenu.append(*mi);
210     mi->signal_activate().connect(rem);
211     mi->show();
212     _GlyphsContextMenu.accelerate(parent);
215 void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
217     Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
218     _KerningPairsContextMenu.append(*mi);
219     mi->signal_activate().connect(rem);
220     mi->show();
221     _KerningPairsContextMenu.accelerate(parent);
224 void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
226     Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
227     _FontsContextMenu.append(*mi);
228     mi->signal_activate().connect(rem);
229     mi->show();
230     _FontsContextMenu.accelerate(parent);
233 void SvgFontsDialog::update_sensitiveness(){
234     if (get_selected_spfont()){
235         global_vbox.set_sensitive(true);
236         glyphs_vbox.set_sensitive(true);
237         kerning_vbox.set_sensitive(true);
238     } else {
239         global_vbox.set_sensitive(false);
240         glyphs_vbox.set_sensitive(false);
241         kerning_vbox.set_sensitive(false);
242     }
245 /* Add all fonts in the document to the combobox. */
246 void SvgFontsDialog::update_fonts()
248     SPDesktop* desktop = this->getDesktop();
249     SPDocument* document = sp_desktop_document(desktop);
250     const GSList* fonts = document->getResourceList("font");
252     _model->clear();
253     for(const GSList *l = fonts; l; l = l->next) {
254         Gtk::TreeModel::Row row = *_model->append();
255         SPFont* f = (SPFont*)l->data;
256         row[_columns.spfont] = f;
257         row[_columns.svgfont] = new SvgFont(f);
258         const gchar* lbl = f->label();
259         const gchar* id = f->getId();
260         row[_columns.label] = lbl ? lbl : (id ? id : "font");
261     }
263     update_sensitiveness();
266 void SvgFontsDialog::on_preview_text_changed(){
267     _font_da.set_text((gchar*) _preview_entry.get_text().c_str());
268     _font_da.set_text(_preview_entry.get_text());
271 void SvgFontsDialog::on_kerning_pair_selection_changed(){
272     SPGlyphKerning* kern = get_selected_kerning_pair();
273     if (!kern) {
274         kerning_preview.set_text("");
275         return;
276     }
277     Glib::ustring str;
278     str += kern->u1->sample_glyph();
279     str += kern->u2->sample_glyph();
281     kerning_preview.set_text(str);
282     this->kerning_pair = kern;
284     //slider values increase from right to left so that they match the kerning pair preview
285     kerning_slider.set_value(get_selected_spfont()->horiz_adv_x - kern->k);
288 void SvgFontsDialog::update_global_settings_tab(){
289     SPFont* font = get_selected_spfont();
290     if (!font) return;
292     SPObject* obj;
293     for (obj=font->children; obj; obj=obj->next){
294         if (SP_IS_FONTFACE(obj)){
295             _familyname_entry->set_text(((SPFontFace*) obj)->font_family);
296         }
297     }
300 void SvgFontsDialog::on_font_selection_changed(){
301     SPFont* spfont = this->get_selected_spfont();
302     if (!spfont) return;
304     SvgFont* svgfont = this->get_selected_svgfont();
305     first_glyph.update(spfont);
306     second_glyph.update(spfont);
307     kerning_preview.set_svgfont(svgfont);
308     _font_da.set_svgfont(svgfont);
309     _font_da.redraw();
311     double set_width = spfont->horiz_adv_x;
312     setwidth_spin.set_value(set_width);
314     kerning_slider.set_range(0, set_width);
315     kerning_slider.set_draw_value(false);
316     kerning_slider.set_value(0);
318     update_global_settings_tab();
319     populate_glyphs_box();
320     populate_kerning_pairs_box();
321     update_sensitiveness();
324 void SvgFontsDialog::on_setwidth_changed(){
325     SPFont* spfont = this->get_selected_spfont();
326     if (spfont){
327         spfont->horiz_adv_x = setwidth_spin.get_value();
328         //TODO: tell cairo that the glyphs cache has to be invalidated
329         //    The current solution is to recreate the whole cairo svgfont.
330         //    This is not a good solution to the issue because big fonts will result in poor performance.
331         update_glyphs();
332     }
335 SPGlyphKerning* SvgFontsDialog::get_selected_kerning_pair()
337     Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
338     if(i)
339         return (*i)[_KerningPairsListColumns.spnode];
340     return NULL;
343 SvgFont* SvgFontsDialog::get_selected_svgfont()
345     Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
346     if(i)
347         return (*i)[_columns.svgfont];
348     return NULL;
351 SPFont* SvgFontsDialog::get_selected_spfont()
353     Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
354     if(i)
355         return (*i)[_columns.spfont];
356     return NULL;
359 SPGlyph* SvgFontsDialog::get_selected_glyph()
361     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
362     if(i)
363         return (*i)[_GlyphsListColumns.glyph_node];
364     return NULL;
367 Gtk::VBox* SvgFontsDialog::global_settings_tab(){
368     _familyname_entry = new AttrEntry(this, (gchar*) _("Family Name:"), SP_PROP_FONT_FAMILY);
370     global_vbox.pack_start(*_familyname_entry, false, false);
371 /*    global_vbox->add(*AttrCombo((gchar*) _("Style:"), SP_PROP_FONT_STYLE));
372     global_vbox->add(*AttrCombo((gchar*) _("Variant:"), SP_PROP_FONT_VARIANT));
373     global_vbox->add(*AttrCombo((gchar*) _("Weight:"), SP_PROP_FONT_WEIGHT));
374 */
376 //Set Width (horiz_adv_x):
377     Gtk::HBox* setwidth_hbox = Gtk::manage(new Gtk::HBox());
378     setwidth_hbox->add(*Gtk::manage(new Gtk::Label(_("Set width:"))));
379     setwidth_hbox->add(setwidth_spin);
381     setwidth_spin.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_setwidth_changed));
382     setwidth_spin.set_range(0, 4096);
383     setwidth_spin.set_increments(10, 0);
384     global_vbox.pack_start(*setwidth_hbox, false, false);
386     return &global_vbox;
389 void
390 SvgFontsDialog::populate_glyphs_box()
392     if (!_GlyphsListStore) return;
393     _GlyphsListStore->clear();
395     SPFont* spfont = this->get_selected_spfont();
396     _glyphs_observer.set(spfont);
398     for(SPObject* node = spfont->children; node; node=node->next){
399         if (SP_IS_GLYPH(node)){
400             Gtk::TreeModel::Row row = *(_GlyphsListStore->append());
401             row[_GlyphsListColumns.glyph_node] = (SPGlyph*) node;
402             row[_GlyphsListColumns.glyph_name] = ((SPGlyph*) node)->glyph_name;
403             row[_GlyphsListColumns.unicode] = ((SPGlyph*) node)->unicode;
404         }
405     }
408 void
409 SvgFontsDialog::populate_kerning_pairs_box()
411     if (!_KerningPairsListStore) return;
412     _KerningPairsListStore->clear();
414     SPFont* spfont = this->get_selected_spfont();
416     for(SPObject* node = spfont->children; node; node=node->next){
417         if (SP_IS_HKERN(node)){
418             Gtk::TreeModel::Row row = *(_KerningPairsListStore->append());
419             row[_KerningPairsListColumns.first_glyph] = ((SPGlyphKerning*) node)->u1->attribute_string().c_str();
420             row[_KerningPairsListColumns.second_glyph] = ((SPGlyphKerning*) node)->u2->attribute_string().c_str();
421             row[_KerningPairsListColumns.kerning_value] = ((SPGlyphKerning*) node)->k;
422             row[_KerningPairsListColumns.spnode] = (SPGlyphKerning*) node;
423         }
424     }
427 SPGlyph *new_glyph(SPDocument* document, SPFont *font, const int count)
429     g_return_val_if_fail(font != NULL, NULL);
430     Inkscape::XML::Document *xml_doc = document->getReprDoc();
432     // create a new glyph
433     Inkscape::XML::Node *repr;
434     repr = xml_doc->createElement("svg:glyph");
436     std::ostringstream os;
437     os << _("glyph") << " " << count;
438     repr->setAttribute("glyph-name", os.str().c_str());
440     // Append the new glyph node to the current font
441     SP_OBJECT_REPR(font)->appendChild(repr);
442     Inkscape::GC::release(repr);
444     // get corresponding object
445     SPGlyph *g = SP_GLYPH( document->getObjectByRepr(repr) );
447     g_assert(g != NULL);
448     g_assert(SP_IS_GLYPH(g));
450     return g;
453 void SvgFontsDialog::update_glyphs(){
454     SPFont* font = get_selected_spfont();
455     if (!font) return;
456     populate_glyphs_box();
457     populate_kerning_pairs_box();
458     first_glyph.update(font);
459     second_glyph.update(font);
460     get_selected_svgfont()->refresh();
461     _font_da.redraw();
464 void SvgFontsDialog::add_glyph(){
465     const int count = _GlyphsListStore->children().size();
466     SPDocument* doc = sp_desktop_document(this->getDesktop());
467     /* SPGlyph* glyph =*/ new_glyph(doc, get_selected_spfont(), count+1);
469     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add glyph"));
471     update_glyphs();
474 void SvgFontsDialog::set_glyph_description_from_selected_path(){
475     SPDesktop* desktop = this->getDesktop();
476     if (!desktop) {
477         g_warning("SvgFontsDialog: No active desktop");
478         return;
479     }
481     Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
482     SPDocument* doc = sp_desktop_document(desktop);
483     Inkscape::Selection* sel = sp_desktop_selection(desktop);
484     if (sel->isEmpty()){
485         char *msg = _("Select a <b>path</b> to define the curves of a glyph");
486         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
487         return;
488     }
490     Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
491     if (!node) return;//TODO: should this be an assert?
492     if (!node->matchAttributeName("d") || !node->attribute("d")){
493         char *msg = _("The selected object does not have a <b>path</b> description.");
494         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
495         return;
496     } //TODO: //Is there a better way to tell it to to the user?
498     Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
500     //This matrix flips the glyph vertically
501     Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
502     pathv*=m;
503     //then we offset it
504     pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
506     SPGlyph* glyph = get_selected_glyph();
507     if (!glyph){
508         char *msg = _("No glyph selected in the SVGFonts dialog.");
509         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
510         return;
511     }
512         //XML Tree being directly used here while it shouldn't be.
513     glyph->getRepr()->setAttribute("d", (char*) sp_svg_write_path (pathv));
514     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
516     update_glyphs();
519 void SvgFontsDialog::missing_glyph_description_from_selected_path(){
520     SPDesktop* desktop = this->getDesktop();
521     if (!desktop) {
522         g_warning("SvgFontsDialog: No active desktop");
523         return;
524     }
526     Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
527     SPDocument* doc = sp_desktop_document(desktop);
528     Inkscape::Selection* sel = sp_desktop_selection(desktop);
529     if (sel->isEmpty()){
530         char *msg = _("Select a <b>path</b> to define the curves of a glyph");
531         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
532         return;
533     }
535     Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
536     if (!node) return;//TODO: should this be an assert?
537     if (!node->matchAttributeName("d") || !node->attribute("d")){
538         char *msg = _("The selected object does not have a <b>path</b> description.");
539         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
540         return;
541     } //TODO: //Is there a better way to tell it to to the user?
543     Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
545     //This matrix flips the glyph vertically
546     Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
547     pathv*=m;
548     //then we offset it
549 //  pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
550     pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(1000));//TODO: use here the units-per-em attribute?
552     SPObject* obj;
553     for (obj = get_selected_spfont()->children; obj; obj=obj->next){
554         if (SP_IS_MISSING_GLYPH(obj)){
556             //XML Tree being directly used here while it shouldn't be.
557             obj->getRepr()->setAttribute("d", (char*) sp_svg_write_path (pathv));
558             DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
559         }
560     }
562     update_glyphs();
565 void SvgFontsDialog::reset_missing_glyph_description(){
566     SPDesktop* desktop = this->getDesktop();
567     if (!desktop) {
568         g_warning("SvgFontsDialog: No active desktop");
569         return;
570     }
572     SPDocument* doc = sp_desktop_document(desktop);
573     SPObject* obj;
574     for (obj = get_selected_spfont()->children; obj; obj=obj->next){
575         if (SP_IS_MISSING_GLYPH(obj)){
576             //XML Tree being directly used here while it shouldn't be.
577             obj->getRepr()->setAttribute("d", (char*) "M0,0h1000v1024h-1000z");
578             DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Reset missing-glyph"));
579         }
580     }
582     update_glyphs();
585 void SvgFontsDialog::glyph_name_edit(const Glib::ustring&, const Glib::ustring& str){
586     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
587     if (!i) return;
589     SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
590     //XML Tree being directly used here while it shouldn't be.
591     glyph->getRepr()->setAttribute("glyph-name", str.c_str());
593     SPDocument* doc = sp_desktop_document(this->getDesktop());
594     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Edit glyph name"));
596     update_glyphs();
599 void SvgFontsDialog::glyph_unicode_edit(const Glib::ustring&, const Glib::ustring& str){
600     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
601     if (!i) return;
603     SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
604     //XML Tree being directly used here while it shouldn't be.
605     glyph->getRepr()->setAttribute("unicode", str.c_str());
607     SPDocument* doc = sp_desktop_document(this->getDesktop());
608     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph unicode"));
610     update_glyphs();
613 void SvgFontsDialog::remove_selected_font(){
614     SPFont* font = get_selected_spfont();
615     if (!font) return;
617     //XML Tree being directly used here while it shouldn't be.
618     sp_repr_unparent(font->getRepr());
619     SPDocument* doc = sp_desktop_document(this->getDesktop());
620     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove font"));
622     update_fonts();
625 void SvgFontsDialog::remove_selected_glyph(){
626     if(!_GlyphsList.get_selection()) return;
628     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
629     if(!i) return;
631     SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
633         //XML Tree being directly used here while it shouldn't be.
634     sp_repr_unparent(glyph->getRepr());
636     SPDocument* doc = sp_desktop_document(this->getDesktop());
637     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove glyph"));
639     update_glyphs();
642 void SvgFontsDialog::remove_selected_kerning_pair(){
643     if(!_KerningPairsList.get_selection()) return;
645     Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
646     if(!i) return;
648     SPGlyphKerning* pair = (*i)[_KerningPairsListColumns.spnode];
650         //XML Tree being directly used here while it shouldn't be.
651     sp_repr_unparent(pair->getRepr());
653     SPDocument* doc = sp_desktop_document(this->getDesktop());
654     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove kerning pair"));
656     update_glyphs();
659 Gtk::VBox* SvgFontsDialog::glyphs_tab(){
660     _GlyphsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::glyphs_list_button_release));
661     create_glyphs_popup_menu(_GlyphsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_glyph));
663     Gtk::HBox* missing_glyph_hbox = Gtk::manage(new Gtk::HBox());
664     Gtk::Label* missing_glyph_label = Gtk::manage(new Gtk::Label(_("Missing Glyph:")));
665     missing_glyph_hbox->pack_start(*missing_glyph_label, false,false);
666     missing_glyph_hbox->pack_start(missing_glyph_button, false,false);
667     missing_glyph_hbox->pack_start(missing_glyph_reset_button, false,false);
668     missing_glyph_button.set_label(_("From selection..."));
669     missing_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::missing_glyph_description_from_selected_path));
670     missing_glyph_reset_button.set_label(_("Reset"));
671     missing_glyph_reset_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::reset_missing_glyph_description));
673     glyphs_vbox.pack_start(*missing_glyph_hbox, false,false);
675     glyphs_vbox.add(_GlyphsListScroller);
676     _GlyphsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
677     _GlyphsListScroller.set_size_request(-1, 290);//It seems that is does not work. Why? I want a box with larger height
678     _GlyphsListScroller.add(_GlyphsList);
679     _GlyphsListStore = Gtk::ListStore::create(_GlyphsListColumns);
680     _GlyphsList.set_model(_GlyphsListStore);
681     _GlyphsList.append_column_editable(_("Glyph name"), _GlyphsListColumns.glyph_name);
682     _GlyphsList.append_column_editable(_("Matching string"), _GlyphsListColumns.unicode);
684     Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
685     add_glyph_button.set_label(_("Add Glyph"));
686     add_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_glyph));
688     hb->pack_start(add_glyph_button, false,false);
689     hb->pack_start(glyph_from_path_button, false,false);
691     glyphs_vbox.pack_start(*hb, false, false);
692     glyph_from_path_button.set_label(_("Get curves from selection..."));
693     glyph_from_path_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::set_glyph_description_from_selected_path));
695     dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(0))->signal_edited().connect(
696         sigc::mem_fun(*this, &SvgFontsDialog::glyph_name_edit));
698     dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(1))->signal_edited().connect(
699         sigc::mem_fun(*this, &SvgFontsDialog::glyph_unicode_edit));
701     _glyphs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_glyphs));
703     return &glyphs_vbox;
706 void SvgFontsDialog::add_kerning_pair(){
707     if (first_glyph.get_active_text() == "" ||
708         second_glyph.get_active_text() == "") return;
710     //look for this kerning pair on the currently selected font
711     this->kerning_pair = NULL;
712     for(SPObject* node = this->get_selected_spfont()->children; node; node=node->next){
713         //TODO: It is not really correct to get only the first byte of each string.
714         //TODO: We should also support vertical kerning
715         if (SP_IS_HKERN(node) && ((SPGlyphKerning*)node)->u1->contains((gchar) first_glyph.get_active_text().c_str()[0])
716             && ((SPGlyphKerning*)node)->u2->contains((gchar) second_glyph.get_active_text().c_str()[0]) ){
717             this->kerning_pair = (SPGlyphKerning*)node;
718             continue;
719         }
720     }
722     if (this->kerning_pair) return; //We already have this kerning pair
724     SPDocument* document = sp_desktop_document(this->getDesktop());
725     Inkscape::XML::Document *xml_doc = document->getReprDoc();
727     // create a new hkern node
728     Inkscape::XML::Node *repr = xml_doc->createElement("svg:hkern");
730     repr->setAttribute("u1", first_glyph.get_active_text().c_str());
731     repr->setAttribute("u2", second_glyph.get_active_text().c_str());
732     repr->setAttribute("k", "0");
734     // Append the new hkern node to the current font
735     SP_OBJECT_REPR(get_selected_spfont())->appendChild(repr);
736     Inkscape::GC::release(repr);
738     // get corresponding object
739     this->kerning_pair = SP_HKERN( document->getObjectByRepr(repr) );
741     DocumentUndo::done(document, SP_VERB_DIALOG_SVG_FONTS, _("Add kerning pair"));
744 Gtk::VBox* SvgFontsDialog::kerning_tab(){
745     _KerningPairsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::kerning_pairs_list_button_release));
746     create_kerning_pairs_popup_menu(_KerningPairsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_kerning_pair));
748 //Kerning Setup:
749     kerning_vbox.add(*Gtk::manage(new Gtk::Label(_("Kerning Setup"))));
750     Gtk::HBox* kerning_selector = Gtk::manage(new Gtk::HBox());
751     kerning_selector->add(*Gtk::manage(new Gtk::Label(_("1st Glyph:"))));
752     kerning_selector->add(first_glyph);
753     kerning_selector->add(*Gtk::manage(new Gtk::Label(_("2nd Glyph:"))));
754     kerning_selector->add(second_glyph);
755     kerning_selector->add(add_kernpair_button);
756     add_kernpair_button.set_label(_("Add pair"));
757     add_kernpair_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_kerning_pair));
758     _KerningPairsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_pair_selection_changed));
759     kerning_slider.signal_value_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_value_changed));
761     kerning_vbox.pack_start(*kerning_selector, false,false);
763     kerning_vbox.add(_KerningPairsListScroller);
764     _KerningPairsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
765     _KerningPairsListScroller.add(_KerningPairsList);
766     _KerningPairsListStore = Gtk::ListStore::create(_KerningPairsListColumns);
767     _KerningPairsList.set_model(_KerningPairsListStore);
768     _KerningPairsList.append_column(_("First Unicode range"), _KerningPairsListColumns.first_glyph);
769     _KerningPairsList.append_column(_("Second Unicode range"), _KerningPairsListColumns.second_glyph);
770 //    _KerningPairsList.append_column_numeric_editable(_("Kerning Value"), _KerningPairsListColumns.kerning_value, "%f");
772     kerning_vbox.add((Gtk::Widget&) kerning_preview);
774     Gtk::HBox* kerning_amount_hbox = Gtk::manage(new Gtk::HBox());
775     kerning_vbox.pack_start(*kerning_amount_hbox, false,false);
776     kerning_amount_hbox->add(*Gtk::manage(new Gtk::Label(_("Kerning value:"))));
777     kerning_amount_hbox->add(kerning_slider);
779     kerning_preview.set_size(300 + 20, 150 + 20);
780     _font_da.set_size(150 + 20, 50 + 20);
782     return &kerning_vbox;
785 SPFont *new_font(SPDocument *document)
787     g_return_val_if_fail(document != NULL, NULL);
789     SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
791     Inkscape::XML::Document *xml_doc = document->getReprDoc();
793     // create a new font
794     Inkscape::XML::Node *repr = xml_doc->createElement("svg:font");
796     //By default, set the horizontal advance to 1024 units
797     repr->setAttribute("horiz-adv-x", "1024");
799     // Append the new font node to defs
800     SP_OBJECT_REPR(defs)->appendChild(repr);
802     //create a missing glyph
803     Inkscape::XML::Node *fontface;
804     fontface = xml_doc->createElement("svg:font-face");
805     fontface->setAttribute("units-per-em", "1024");
806     repr->appendChild(fontface);
808     //create a missing glyph
809     Inkscape::XML::Node *mg;
810     mg = xml_doc->createElement("svg:missing-glyph");
811     mg->setAttribute("d", "M0,0h1000v1024h-1000z");
812     repr->appendChild(mg);
814     // get corresponding object
815     SPFont *f = SP_FONT( document->getObjectByRepr(repr) );
817     g_assert(f != NULL);
818     g_assert(SP_IS_FONT(f));
819     Inkscape::GC::release(mg);
820     Inkscape::GC::release(repr);
821     return f;
824 void set_font_family(SPFont* font, char* str){
825     if (!font) return;
826     SPObject* obj;
827     for (obj=font->children; obj; obj=obj->next){
828         if (SP_IS_FONTFACE(obj)){
829             //XML Tree being directly used here while it shouldn't be.
830             obj->getRepr()->setAttribute("font-family", str);
831         }
832     }
834     DocumentUndo::done(font->document, SP_VERB_DIALOG_SVG_FONTS, _("Set font family"));
837 void SvgFontsDialog::add_font(){
838     SPDocument* doc = sp_desktop_document(this->getDesktop());
839     SPFont* font = new_font(doc);
841     const int count = _model->children().size();
842     std::ostringstream os, os2;
843     os << _("font") << " " << count;
844     font->setLabel(os.str().c_str());
846     os2 << "SVGFont " << count;
847     SPObject* obj;
848     for (obj=font->children; obj; obj=obj->next){
849         if (SP_IS_FONTFACE(obj)){
850             //XML Tree being directly used here while it shouldn't be.
851             obj->getRepr()->setAttribute("font-family", os2.str().c_str());
852         }
853     }
855     update_fonts();
856 //    select_font(font);
858     DocumentUndo::done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add font"));
861 SvgFontsDialog::SvgFontsDialog()
862  : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), _add(Gtk::Stock::NEW)
864     _add.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_font));
866     Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
867     Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
869     vbox->pack_start(_FontsList);
870     vbox->pack_start(_add, false, false);
871     hbox->add(*vbox);
872     hbox->add(_font_settings);
873     _getContents()->add(*hbox);
875 //List of SVGFonts declared in a document:
876     _model = Gtk::ListStore::create(_columns);
877     _FontsList.set_model(_model);
878     _FontsList.append_column_editable(_("_Font"), _columns.label);
879     _FontsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_font_selection_changed));
881     this->update_fonts();
883     Gtk::Notebook *tabs = Gtk::manage(new Gtk::Notebook());
884     tabs->set_scrollable();
886     tabs->append_page(*global_settings_tab(), _("_Global Settings"), true);
887     tabs->append_page(*glyphs_tab(), _("_Glyphs"), true);
888     tabs->append_page(*kerning_tab(), _("_Kerning"), true);
890     _font_settings.add(*tabs);
892 //Text Preview:
893     _preview_entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_preview_text_changed));
894     _getContents()->add((Gtk::Widget&) _font_da);
895     _preview_entry.set_text(_("Sample Text"));
896     _font_da.set_text(_("Sample Text"));
898     Gtk::HBox* preview_entry_hbox = Gtk::manage(new Gtk::HBox());
899     _getContents()->add(*preview_entry_hbox);
900     preview_entry_hbox->add(*Gtk::manage(new Gtk::Label(_("Preview Text:"))));
901     preview_entry_hbox->add(_preview_entry);
903     _FontsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::fonts_list_button_release));
904     create_fonts_popup_menu(_FontsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_font));
906     _defs_observer.set(SP_DOCUMENT_DEFS(sp_desktop_document(this->getDesktop())));
907     _defs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_fonts));
909     _getContents()->show_all();
912 SvgFontsDialog::~SvgFontsDialog(){}
914 } // namespace Dialog
915 } // namespace UI
916 } // namespace Inkscape
918 #endif //#ifdef ENABLE_SVG_FONTS
920 /*
921   Local Variables:
922   mode:c++
923   c-file-style:"stroustrup"
924   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
925   indent-tabs-mode:nil
926   fill-column:99
927   End:
928 */
929 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :