Code

A simple layout document as to what, why and how is cppification.
[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         SPDocumentUndo::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 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();
183 void SvgFontsDialog::glyphs_list_button_release(GdkEventButton* event)
185     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
186         _GlyphsContextMenu.popup(event->button, event->time);
187     }
190 void SvgFontsDialog::kerning_pairs_list_button_release(GdkEventButton* event)
192     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
193         _KerningPairsContextMenu.popup(event->button, event->time);
194     }
197 void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event)
199     if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
200         _FontsContextMenu.popup(event->button, event->time);
201     }
204 void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
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);
213 void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
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);
222 void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
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);
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     }
243 /* Add all fonts in the document to the combobox. */
244 void SvgFontsDialog::update_fonts()
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();
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());
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);
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     }
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();
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     }
333 SPGlyphKerning* SvgFontsDialog::get_selected_kerning_pair()
335     Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
336     if(i)
337         return (*i)[_KerningPairsListColumns.spnode];
338     return NULL;
341 SvgFont* SvgFontsDialog::get_selected_svgfont()
343     Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
344     if(i)
345         return (*i)[_columns.svgfont];
346     return NULL;
349 SPFont* SvgFontsDialog::get_selected_spfont()
351     Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
352     if(i)
353         return (*i)[_columns.spfont];
354     return NULL;
357 SPGlyph* SvgFontsDialog::get_selected_glyph()
359     Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
360     if(i)
361         return (*i)[_GlyphsListColumns.glyph_node];
362     return NULL;
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;
387 void
388 SvgFontsDialog::populate_glyphs_box()
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     }
406 void
407 SvgFontsDialog::populate_kerning_pairs_box()
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     }
425 SPGlyph *new_glyph(SPDocument* document, SPFont *font, const int count)
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;
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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;
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"));
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;
783 SPFont *new_font(SPDocument *document)
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;
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"));
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"));
860 SvgFontsDialog::SvgFontsDialog()
861  : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), _add(Gtk::Stock::NEW)
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();
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 :