1 /** @file
2 * @brief SVG Fonts dialog - implementation
3 */
4 /* Authors:
5 * Felipe C. da S. Sanches <felipe.sanches@gmail.com>
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 "document-private.h"
18 #include <gtkmm/notebook.h>
19 #include <glibmm/i18n.h>
20 #include "selection.h"
21 #include <string.h>
22 #include "svg-fonts-dialog.h"
23 #include "xml/node.h"
24 #include "xml/repr.h"
25 #include "svg/svg.h"
26 #include <2geom/pathvector.h>
28 SvgFontDrawingArea::SvgFontDrawingArea(){
29 this->text = "";
30 this->svgfont = NULL;
31 }
33 void SvgFontDrawingArea::set_svgfont(SvgFont* svgfont){
34 this->svgfont = svgfont;
35 }
37 void SvgFontDrawingArea::set_text(Glib::ustring text){
38 this->text = text;
39 redraw();
40 }
42 void SvgFontDrawingArea::set_size(int x, int y){
43 this->x = x;
44 this->y = y;
45 ((Gtk::Widget*) this)->set_size_request(x, y);
46 }
48 void SvgFontDrawingArea::redraw(){
49 ((Gtk::Widget*) this)->queue_draw();
50 }
52 bool SvgFontDrawingArea::on_expose_event (GdkEventExpose */*event*/){
53 if (this->svgfont){
54 Glib::RefPtr<Gdk::Window> window = get_window();
55 Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
56 cr->set_font_face( Cairo::RefPtr<Cairo::FontFace>(new Cairo::FontFace(this->svgfont->get_font_face(), false /* does not have reference */)) );
57 cr->set_font_size (this->y-20);
58 cr->move_to (10, 10);
59 cr->show_text (this->text.c_str());
60 }
61 return TRUE;
62 }
64 namespace Inkscape {
65 namespace UI {
66 namespace Dialog {
68 /*
69 Gtk::HBox* SvgFontsDialog::AttrEntry(gchar* lbl, const SPAttributeEnum attr){
70 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
71 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
72 Gtk::Entry* entry = Gtk::manage(new Gtk::Entry());
73 hbox->add(* entry );
74 hbox->show_all();
76 entry->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_attr_changed));
77 return hbox;
78 }
79 */
81 SvgFontsDialog::AttrEntry::AttrEntry(SvgFontsDialog* d, gchar* lbl, const SPAttributeEnum attr){
82 this->dialog = d;
83 this->attr = attr;
84 this->add(* Gtk::manage(new Gtk::Label(lbl)) );
85 this->add(entry);
86 this->show_all();
88 entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::AttrEntry::on_attr_changed));
89 }
91 void SvgFontsDialog::AttrEntry::set_text(char* t){
92 if (!t) return;
93 entry.set_text(t);
94 }
96 void SvgFontsDialog::AttrEntry::on_attr_changed(){
98 SPObject* o = NULL;
99 for(SPObject* node = this->dialog->get_selected_spfont()->children; node; node=node->next){
100 switch(this->attr){
101 case SP_PROP_FONT_FAMILY:
102 if (SP_IS_FONTFACE(node)){
103 o = node;
104 continue;
105 }
106 break;
107 default:
108 o = NULL;
109 }
110 }
112 const gchar* name = (const gchar*)sp_attribute_name(this->attr);
113 if(name && o) {
114 SP_OBJECT_REPR(o)->setAttribute((const gchar*) name, this->entry.get_text().c_str());
115 o->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
117 Glib::ustring undokey = "svgfonts:";
118 undokey += name;
119 sp_document_maybe_done(o->document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS,
120 _("Set SVG Font attribute"));
121 }
123 }
125 Gtk::HBox* SvgFontsDialog::AttrCombo(gchar* lbl, const SPAttributeEnum /*attr*/){
126 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
127 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
128 hbox->add(* Gtk::manage(new Gtk::ComboBox()) );
129 hbox->show_all();
130 return hbox;
131 }
133 /*
134 Gtk::HBox* SvgFontsDialog::AttrSpin(gchar* lbl){
135 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
136 hbox->add(* Gtk::manage(new Gtk::Label(lbl)) );
137 hbox->add(* Gtk::manage(new Gtk::SpinBox()) );
138 hbox->show_all();
139 return hbox;
140 }*/
142 /*** SvgFontsDialog ***/
144 GlyphComboBox::GlyphComboBox(){
145 }
147 void GlyphComboBox::update(SPFont* spfont){
148 if (!spfont) return
149 //TODO: figure out why do we need to append_text("") before clearing items properly...
151 this->append_text(""); //Gtk is refusing to clear the combobox when I comment out this line
152 this->clear_items();
154 for(SPObject* node = spfont->children; node; node=node->next){
155 if (SP_IS_GLYPH(node)){
156 this->append_text(((SPGlyph*)node)->unicode);
157 }
158 }
159 }
161 void SvgFontsDialog::on_kerning_value_changed(){
162 if (!this->kerning_pair) return;
163 SPDocument* document = sp_desktop_document(this->getDesktop());
165 //TODO: I am unsure whether this is the correct way of calling sp_document_maybe_done
166 Glib::ustring undokey = "svgfonts:hkern:k:";
167 undokey += this->kerning_pair->u1->attribute_string();
168 undokey += ":";
169 undokey += this->kerning_pair->u2->attribute_string();
171 //slider values increase from right to left so that they match the kerning pair preview
172 this->kerning_pair->repr->setAttribute("k", Glib::Ascii::dtostr(get_selected_spfont()->horiz_adv_x - kerning_slider.get_value()).c_str());
173 sp_document_maybe_done(document, undokey.c_str(), SP_VERB_DIALOG_SVG_FONTS, _("Adjust kerning value"));
175 //populate_kerning_pairs_box();
176 kerning_preview.redraw();
177 _font_da.redraw();
178 }
180 void SvgFontsDialog::glyphs_list_button_release(GdkEventButton* event)
181 {
182 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
183 _GlyphsContextMenu.popup(event->button, event->time);
184 }
185 }
187 void SvgFontsDialog::kerning_pairs_list_button_release(GdkEventButton* event)
188 {
189 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
190 _KerningPairsContextMenu.popup(event->button, event->time);
191 }
192 }
194 void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event)
195 {
196 if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) {
197 _FontsContextMenu.popup(event->button, event->time);
198 }
199 }
201 void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
202 {
203 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
204 _GlyphsContextMenu.append(*mi);
205 mi->signal_activate().connect(rem);
206 mi->show();
207 _GlyphsContextMenu.accelerate(parent);
208 }
210 void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
211 {
212 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
213 _KerningPairsContextMenu.append(*mi);
214 mi->signal_activate().connect(rem);
215 mi->show();
216 _KerningPairsContextMenu.accelerate(parent);
217 }
219 void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem)
220 {
221 Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE));
222 _FontsContextMenu.append(*mi);
223 mi->signal_activate().connect(rem);
224 mi->show();
225 _FontsContextMenu.accelerate(parent);
226 }
228 void SvgFontsDialog::update_sensitiveness(){
229 if (get_selected_spfont()){
230 global_vbox.set_sensitive(true);
231 glyphs_vbox.set_sensitive(true);
232 kerning_vbox.set_sensitive(true);
233 } else {
234 global_vbox.set_sensitive(false);
235 glyphs_vbox.set_sensitive(false);
236 kerning_vbox.set_sensitive(false);
237 }
238 }
240 /* Add all fonts in the document to the combobox. */
241 void SvgFontsDialog::update_fonts()
242 {
243 SPDesktop* desktop = this->getDesktop();
244 SPDocument* document = sp_desktop_document(desktop);
245 const GSList* fonts = sp_document_get_resource_list(document, "font");
247 _model->clear();
248 for(const GSList *l = fonts; l; l = l->next) {
249 Gtk::TreeModel::Row row = *_model->append();
250 SPFont* f = (SPFont*)l->data;
251 row[_columns.spfont] = f;
252 row[_columns.svgfont] = new SvgFont(f);
253 const gchar* lbl = f->label();
254 const gchar* id = SP_OBJECT_ID(f);
255 row[_columns.label] = lbl ? lbl : (id ? id : "font");
256 }
258 update_sensitiveness();
259 }
261 void SvgFontsDialog::on_preview_text_changed(){
262 _font_da.set_text((gchar*) _preview_entry.get_text().c_str());
263 _font_da.set_text(_preview_entry.get_text());
264 }
266 void SvgFontsDialog::on_kerning_pair_selection_changed(){
267 SPGlyphKerning* kern = get_selected_kerning_pair();
268 if (!kern) {
269 kerning_preview.set_text("");
270 return;
271 }
272 Glib::ustring str;
273 str += kern->u1->sample_glyph();
274 str += kern->u2->sample_glyph();
276 kerning_preview.set_text(str);
277 this->kerning_pair = kern;
279 //slider values increase from right to left so that they match the kerning pair preview
280 kerning_slider.set_value(get_selected_spfont()->horiz_adv_x - kern->k);
281 }
283 void SvgFontsDialog::update_global_settings_tab(){
284 SPFont* font = get_selected_spfont();
285 if (!font) return;
287 SPObject* obj;
288 for (obj=font->children; obj; obj=obj->next){
289 if (SP_IS_FONTFACE(obj)){
290 _familyname_entry->set_text(((SPFontFace*) obj)->font_family);
291 }
292 }
293 }
295 void SvgFontsDialog::on_font_selection_changed(){
296 SPFont* spfont = this->get_selected_spfont();
297 if (!spfont) return;
299 SvgFont* svgfont = this->get_selected_svgfont();
300 first_glyph.update(spfont);
301 second_glyph.update(spfont);
302 kerning_preview.set_svgfont(svgfont);
303 _font_da.set_svgfont(svgfont);
304 _font_da.redraw();
306 double set_width = spfont->horiz_adv_x;
307 setwidth_spin.set_value(set_width);
309 kerning_slider.set_range(0, set_width);
310 kerning_slider.set_draw_value(false);
311 kerning_slider.set_value(0);
313 update_global_settings_tab();
314 populate_glyphs_box();
315 populate_kerning_pairs_box();
316 update_sensitiveness();
317 }
319 void SvgFontsDialog::on_setwidth_changed(){
320 SPFont* spfont = this->get_selected_spfont();
321 if (spfont){
322 spfont->horiz_adv_x = setwidth_spin.get_value();
323 //TODO: tell cairo that the glyphs cache has to be invalidated
324 // The current solution is to recreate the whole cairo svgfont.
325 // This is not a good solution to the issue because big fonts will result in poor performance.
326 update_glyphs();
327 }
328 }
330 SPGlyphKerning* SvgFontsDialog::get_selected_kerning_pair()
331 {
332 Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
333 if(i)
334 return (*i)[_KerningPairsListColumns.spnode];
335 return NULL;
336 }
338 SvgFont* SvgFontsDialog::get_selected_svgfont()
339 {
340 Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
341 if(i)
342 return (*i)[_columns.svgfont];
343 return NULL;
344 }
346 SPFont* SvgFontsDialog::get_selected_spfont()
347 {
348 Gtk::TreeModel::iterator i = _FontsList.get_selection()->get_selected();
349 if(i)
350 return (*i)[_columns.spfont];
351 return NULL;
352 }
354 SPGlyph* SvgFontsDialog::get_selected_glyph()
355 {
356 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
357 if(i)
358 return (*i)[_GlyphsListColumns.glyph_node];
359 return NULL;
360 }
362 Gtk::VBox* SvgFontsDialog::global_settings_tab(){
363 _familyname_entry = new AttrEntry(this, (gchar*) _("Family Name:"), SP_PROP_FONT_FAMILY);
365 global_vbox.pack_start(*_familyname_entry, false, false);
366 /* global_vbox->add(*AttrCombo((gchar*) _("Style:"), SP_PROP_FONT_STYLE));
367 global_vbox->add(*AttrCombo((gchar*) _("Variant:"), SP_PROP_FONT_VARIANT));
368 global_vbox->add(*AttrCombo((gchar*) _("Weight:"), SP_PROP_FONT_WEIGHT));
369 */
371 //Set Width (horiz_adv_x):
372 Gtk::HBox* setwidth_hbox = Gtk::manage(new Gtk::HBox());
373 setwidth_hbox->add(*Gtk::manage(new Gtk::Label(_("Set width:"))));
374 setwidth_hbox->add(setwidth_spin);
376 setwidth_spin.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_setwidth_changed));
377 setwidth_spin.set_range(0, 4096);
378 setwidth_spin.set_increments(10, 100);
379 global_vbox.pack_start(*setwidth_hbox, false, false);
381 return &global_vbox;
382 }
384 void
385 SvgFontsDialog::populate_glyphs_box()
386 {
387 if (!_GlyphsListStore) return;
388 _GlyphsListStore->clear();
390 SPFont* spfont = this->get_selected_spfont();
391 _glyphs_observer.set(spfont);
393 for(SPObject* node = spfont->children; node; node=node->next){
394 if (SP_IS_GLYPH(node)){
395 Gtk::TreeModel::Row row = *(_GlyphsListStore->append());
396 row[_GlyphsListColumns.glyph_node] = (SPGlyph*) node;
397 row[_GlyphsListColumns.glyph_name] = ((SPGlyph*) node)->glyph_name;
398 row[_GlyphsListColumns.unicode] = ((SPGlyph*) node)->unicode;
399 }
400 }
401 }
403 void
404 SvgFontsDialog::populate_kerning_pairs_box()
405 {
406 if (!_KerningPairsListStore) return;
407 _KerningPairsListStore->clear();
409 SPFont* spfont = this->get_selected_spfont();
411 for(SPObject* node = spfont->children; node; node=node->next){
412 if (SP_IS_HKERN(node)){
413 Gtk::TreeModel::Row row = *(_KerningPairsListStore->append());
414 row[_KerningPairsListColumns.first_glyph] = ((SPGlyphKerning*) node)->u1->attribute_string().c_str();
415 row[_KerningPairsListColumns.second_glyph] = ((SPGlyphKerning*) node)->u2->attribute_string().c_str();
416 row[_KerningPairsListColumns.kerning_value] = ((SPGlyphKerning*) node)->k;
417 row[_KerningPairsListColumns.spnode] = (SPGlyphKerning*) node;
418 }
419 }
420 }
422 SPGlyph *new_glyph(SPDocument* document, SPFont *font, const int count)
423 {
424 g_return_val_if_fail(font != NULL, NULL);
425 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
427 // create a new glyph
428 Inkscape::XML::Node *repr;
429 repr = xml_doc->createElement("svg:glyph");
431 std::ostringstream os;
432 os << _("glyph") << " " << count;
433 repr->setAttribute("glyph-name", os.str().c_str());
435 // Append the new glyph node to the current font
436 SP_OBJECT_REPR(font)->appendChild(repr);
437 Inkscape::GC::release(repr);
439 // get corresponding object
440 SPGlyph *g = SP_GLYPH( document->getObjectByRepr(repr) );
442 g_assert(g != NULL);
443 g_assert(SP_IS_GLYPH(g));
445 return g;
446 }
448 void SvgFontsDialog::update_glyphs(){
449 SPFont* font = get_selected_spfont();
450 if (!font) return;
451 populate_glyphs_box();
452 populate_kerning_pairs_box();
453 first_glyph.update(font);
454 second_glyph.update(font);
455 get_selected_svgfont()->refresh();
456 _font_da.redraw();
457 }
459 void SvgFontsDialog::add_glyph(){
460 const int count = _GlyphsListStore->children().size();
461 SPDocument* doc = sp_desktop_document(this->getDesktop());
462 /* SPGlyph* glyph =*/ new_glyph(doc, get_selected_spfont(), count+1);
464 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add glyph"));
466 update_glyphs();
467 }
469 void SvgFontsDialog::set_glyph_description_from_selected_path(){
470 SPDocument* doc = sp_desktop_document(this->getDesktop());
471 Inkscape::Selection* sel = sp_desktop_selection(this->getDesktop());
472 if (sel->isEmpty()) return;
473 Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
474 if (!node || !node->matchAttributeName("d")) return;
475 if (!node->attribute("d")) return; //TODO: give a message to the user
477 Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
479 //This matrix flips the glyph vertically
480 Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
481 pathv*=m;
482 //then we offset it
483 pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
485 get_selected_glyph()->repr->setAttribute("d", (char*) sp_svg_write_path (pathv));
486 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
488 update_glyphs();
489 }
491 void SvgFontsDialog::missing_glyph_description_from_selected_path(){
492 SPDocument* doc = sp_desktop_document(this->getDesktop());
493 Inkscape::Selection* sel = sp_desktop_selection(this->getDesktop());
494 if (sel->isEmpty()) return;
495 Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0);
496 if (!node || !node->matchAttributeName("d")) return;
497 if (!node->attribute("d")) return; //TODO: give a message to the user
499 Geom::PathVector pathv = sp_svg_read_pathv(node->attribute("d"));
501 //This matrix flips the glyph vertically
502 Geom::Matrix m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0));
503 pathv*=m;
504 //then we offset it
505 // pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(get_selected_spfont()->horiz_adv_x));
506 pathv+=Geom::Point(Geom::Coord(0),Geom::Coord(1000));//TODO: use here the units-per-em attribute?
508 SPObject* obj;
509 for (obj = get_selected_spfont()->children; obj; obj=obj->next){
510 if (SP_IS_MISSING_GLYPH(obj)){
511 obj->repr->setAttribute("d", (char*) sp_svg_write_path (pathv));
512 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph curves"));
513 }
514 }
516 update_glyphs();
517 }
519 void SvgFontsDialog::glyph_name_edit(const Glib::ustring&, const Glib::ustring& str){
520 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
521 if (!i) return;
523 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
524 glyph->repr->setAttribute("glyph-name", str.c_str());
526 SPDocument* doc = sp_desktop_document(this->getDesktop());
527 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Edit glyph name"));
529 update_glyphs();
530 }
532 void SvgFontsDialog::glyph_unicode_edit(const Glib::ustring&, const Glib::ustring& str){
533 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
534 if (!i) return;
536 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
537 glyph->repr->setAttribute("unicode", str.c_str());
539 SPDocument* doc = sp_desktop_document(this->getDesktop());
540 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Set glyph unicode"));
542 update_glyphs();
543 }
545 void SvgFontsDialog::remove_selected_font(){
546 SPFont* font = get_selected_spfont();
548 sp_repr_unparent(font->repr);
549 SPDocument* doc = sp_desktop_document(this->getDesktop());
550 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove font"));
552 update_fonts();
553 }
555 void SvgFontsDialog::remove_selected_glyph(){
556 if(!_GlyphsList.get_selection()) return;
558 Gtk::TreeModel::iterator i = _GlyphsList.get_selection()->get_selected();
559 if(!i) return;
561 SPGlyph* glyph = (*i)[_GlyphsListColumns.glyph_node];
562 sp_repr_unparent(glyph->repr);
564 SPDocument* doc = sp_desktop_document(this->getDesktop());
565 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove glyph"));
567 update_glyphs();
568 }
570 void SvgFontsDialog::remove_selected_kerning_pair(){
571 if(!_KerningPairsList.get_selection()) return;
573 Gtk::TreeModel::iterator i = _KerningPairsList.get_selection()->get_selected();
574 if(!i) return;
576 SPGlyphKerning* pair = (*i)[_KerningPairsListColumns.spnode];
577 sp_repr_unparent(pair->repr);
579 SPDocument* doc = sp_desktop_document(this->getDesktop());
580 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Remove kerning pair"));
582 update_glyphs();
583 }
585 Gtk::VBox* SvgFontsDialog::glyphs_tab(){
586 _GlyphsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::glyphs_list_button_release));
587 create_glyphs_popup_menu(_GlyphsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_glyph));
589 Gtk::HBox* missing_glyph_hbox = Gtk::manage(new Gtk::HBox());
590 Gtk::Label* missing_glyph_label = Gtk::manage(new Gtk::Label(_("Missing Glyph:")));
591 missing_glyph_hbox->pack_start(*missing_glyph_label, false,false);
592 missing_glyph_hbox->pack_start(missing_glyph_button, false,false);
593 missing_glyph_button.set_label(_("From selection..."));
594 missing_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::missing_glyph_description_from_selected_path));
595 glyphs_vbox.pack_start(*missing_glyph_hbox, false,false);
597 glyphs_vbox.add(_GlyphsListScroller);
598 _GlyphsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
599 _GlyphsListScroller.set_size_request(-1, 290);//It seems that is does not work. Why? I want a box with larger height
600 _GlyphsListScroller.add(_GlyphsList);
601 _GlyphsListStore = Gtk::ListStore::create(_GlyphsListColumns);
602 _GlyphsList.set_model(_GlyphsListStore);
603 _GlyphsList.append_column_editable(_("Glyph Name"), _GlyphsListColumns.glyph_name);
604 _GlyphsList.append_column_editable(_("Unicode"), _GlyphsListColumns.unicode);
606 Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
607 add_glyph_button.set_label(_("Add Glyph"));
608 add_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_glyph));
610 hb->pack_start(add_glyph_button, false,false);
611 hb->pack_start(glyph_from_path_button, false,false);
613 glyphs_vbox.pack_start(*hb, false, false);
614 glyph_from_path_button.set_label(_("Get curves from selection..."));
615 glyph_from_path_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::set_glyph_description_from_selected_path));
617 dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(0))->signal_edited().connect(
618 sigc::mem_fun(*this, &SvgFontsDialog::glyph_name_edit));
620 dynamic_cast<Gtk::CellRendererText*>( _GlyphsList.get_column_cell_renderer(1))->signal_edited().connect(
621 sigc::mem_fun(*this, &SvgFontsDialog::glyph_unicode_edit));
623 _glyphs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_glyphs));
625 return &glyphs_vbox;
626 }
628 void SvgFontsDialog::add_kerning_pair(){
629 if (first_glyph.get_active_text() == "" ||
630 second_glyph.get_active_text() == "") return;
632 //look for this kerning pair on the currently selected font
633 this->kerning_pair = NULL;
634 for(SPObject* node = this->get_selected_spfont()->children; node; node=node->next){
635 //TODO: It is not really correct to get only the first byte of each string.
636 //TODO: We should also support vertical kerning
637 if (SP_IS_HKERN(node) && ((SPGlyphKerning*)node)->u1->contains((gchar) first_glyph.get_active_text().c_str()[0])
638 && ((SPGlyphKerning*)node)->u2->contains((gchar) second_glyph.get_active_text().c_str()[0]) ){
639 this->kerning_pair = (SPGlyphKerning*)node;
640 continue;
641 }
642 }
644 if (this->kerning_pair) return; //We already have this kerning pair
646 SPDocument* document = sp_desktop_document(this->getDesktop());
647 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
649 // create a new hkern node
650 Inkscape::XML::Node *repr;
651 repr = xml_doc->createElement("svg:hkern");
653 repr->setAttribute("u1", first_glyph.get_active_text().c_str());
654 repr->setAttribute("u2", second_glyph.get_active_text().c_str());
655 repr->setAttribute("k", "0");
657 // Append the new hkern node to the current font
658 SP_OBJECT_REPR(get_selected_spfont())->appendChild(repr);
659 Inkscape::GC::release(repr);
661 // get corresponding object
662 this->kerning_pair = SP_HKERN( document->getObjectByRepr(repr) );
664 sp_document_done(document, SP_VERB_DIALOG_SVG_FONTS, _("Add kerning pair"));
665 }
667 Gtk::VBox* SvgFontsDialog::kerning_tab(){
668 _KerningPairsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::kerning_pairs_list_button_release));
669 create_kerning_pairs_popup_menu(_KerningPairsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_kerning_pair));
671 //Kerning Setup:
672 kerning_vbox.add(*Gtk::manage(new Gtk::Label(_("Kerning Setup:"))));
673 Gtk::HBox* kerning_selector = Gtk::manage(new Gtk::HBox());
674 kerning_selector->add(*Gtk::manage(new Gtk::Label(_("1st Glyph:"))));
675 kerning_selector->add(first_glyph);
676 kerning_selector->add(*Gtk::manage(new Gtk::Label(_("2nd Glyph:"))));
677 kerning_selector->add(second_glyph);
678 kerning_selector->add(add_kernpair_button);
679 add_kernpair_button.set_label(_("Add pair"));
680 add_kernpair_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_kerning_pair));
681 _KerningPairsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_pair_selection_changed));
682 kerning_slider.signal_value_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_kerning_value_changed));
684 kerning_vbox.pack_start(*kerning_selector, false,false);
686 kerning_vbox.add(_KerningPairsListScroller);
687 _KerningPairsListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
688 _KerningPairsListScroller.add(_KerningPairsList);
689 _KerningPairsListStore = Gtk::ListStore::create(_KerningPairsListColumns);
690 _KerningPairsList.set_model(_KerningPairsListStore);
691 _KerningPairsList.append_column(_("First Unicode range"), _KerningPairsListColumns.first_glyph);
692 _KerningPairsList.append_column(_("Second Unicode range"), _KerningPairsListColumns.second_glyph);
693 // _KerningPairsList.append_column_numeric_editable(_("Kerning Value"), _KerningPairsListColumns.kerning_value, "%f");
695 kerning_vbox.add((Gtk::Widget&) kerning_preview);
697 Gtk::HBox* kerning_amount_hbox = Gtk::manage(new Gtk::HBox());
698 kerning_vbox.pack_start(*kerning_amount_hbox, false,false);
699 kerning_amount_hbox->add(*Gtk::manage(new Gtk::Label(_("Kerning value:"))));
700 kerning_amount_hbox->add(kerning_slider);
702 kerning_preview.set_size(300 + 20, 150 + 20);
703 _font_da.set_size(150 + 20, 50 + 20);
705 return &kerning_vbox;
706 }
708 SPFont *new_font(SPDocument *document)
709 {
710 g_return_val_if_fail(document != NULL, NULL);
712 SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
714 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
716 // create a new font
717 Inkscape::XML::Node *repr;
718 repr = xml_doc->createElement("svg:font");
720 //By default, set the horizontal advance to 1024 units
721 repr->setAttribute("horiz-adv-x", "1024");
723 // Append the new font node to defs
724 SP_OBJECT_REPR(defs)->appendChild(repr);
726 //create a missing glyph
727 Inkscape::XML::Node *fontface;
728 fontface = xml_doc->createElement("svg:font-face");
729 fontface->setAttribute("units-per-em", "1024");
730 repr->appendChild(fontface);
732 //create a missing glyph
733 Inkscape::XML::Node *mg;
734 mg = xml_doc->createElement("svg:missing-glyph");
735 mg->setAttribute("d", "M0,0h1020v1024h-1020z");
736 repr->appendChild(mg);
738 // get corresponding object
739 SPFont *f = SP_FONT( document->getObjectByRepr(repr) );
741 g_assert(f != NULL);
742 g_assert(SP_IS_FONT(f));
743 Inkscape::GC::release(mg);
744 Inkscape::GC::release(repr);
745 return f;
746 }
748 void set_font_family(SPFont* font, char* str){
749 if (!font) return;
750 SPObject* obj;
751 for (obj=font->children; obj; obj=obj->next){
752 if (SP_IS_FONTFACE(obj)){
753 obj->repr->setAttribute("font-family", str);
754 }
755 }
757 sp_document_done(font->document, SP_VERB_DIALOG_SVG_FONTS, _("Set font family"));
758 }
760 void SvgFontsDialog::add_font(){
761 SPDocument* doc = sp_desktop_document(this->getDesktop());
762 SPFont* font = new_font(doc);
764 const int count = _model->children().size();
765 std::ostringstream os, os2;
766 os << _("font") << " " << count;
767 font->setLabel(os.str().c_str());
769 os2 << "SVGFont " << count;
770 SPObject* obj;
771 for (obj=font->children; obj; obj=obj->next){
772 if (SP_IS_FONTFACE(obj)){
773 obj->repr->setAttribute("font-family", os2.str().c_str());
774 }
775 }
777 update_fonts();
778 // select_font(font);
780 sp_document_done(doc, SP_VERB_DIALOG_SVG_FONTS, _("Add font"));
781 }
783 SvgFontsDialog::SvgFontsDialog()
784 : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), _add(Gtk::Stock::NEW)
785 {
786 _add.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_font));
788 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
789 Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
791 vbox->pack_start(_FontsList);
792 vbox->pack_start(_add, false, false);
793 hbox->add(*vbox);
794 hbox->add(_font_settings);
795 _getContents()->add(*hbox);
797 //List of SVGFonts declared in a document:
798 _model = Gtk::ListStore::create(_columns);
799 _FontsList.set_model(_model);
800 _FontsList.append_column_editable(_("_Font"), _columns.label);
801 _FontsList.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_font_selection_changed));
803 this->update_fonts();
805 Gtk::Notebook *tabs = Gtk::manage(new Gtk::Notebook());
806 tabs->set_scrollable();
808 tabs->append_page(*global_settings_tab(), _("_Global Settings"), true);
809 tabs->append_page(*glyphs_tab(), _("_Glyphs"), true);
810 tabs->append_page(*kerning_tab(), _("_Kerning"), true);
812 _font_settings.add(*tabs);
814 //Text Preview:
815 _preview_entry.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::on_preview_text_changed));
816 _getContents()->add((Gtk::Widget&) _font_da);
817 _preview_entry.set_text("Sample Text");
818 _font_da.set_text("Sample Text");
820 Gtk::HBox* preview_entry_hbox = Gtk::manage(new Gtk::HBox());
821 _getContents()->add(*preview_entry_hbox);
822 preview_entry_hbox->add(*Gtk::manage(new Gtk::Label(_("Preview Text:"))));
823 preview_entry_hbox->add(_preview_entry);
825 _FontsList.signal_button_release_event().connect_notify(sigc::mem_fun(*this, &SvgFontsDialog::fonts_list_button_release));
826 create_fonts_popup_menu(_FontsList, sigc::mem_fun(*this, &SvgFontsDialog::remove_selected_font));
828 _defs_observer.set(SP_DOCUMENT_DEFS(sp_desktop_document(this->getDesktop())));
829 _defs_observer.signal_changed().connect(sigc::mem_fun(*this, &SvgFontsDialog::update_fonts));
831 _getContents()->show_all();
832 }
834 SvgFontsDialog::~SvgFontsDialog(){}
836 } // namespace Dialog
837 } // namespace UI
838 } // namespace Inkscape
840 #endif //#ifdef ENABLE_SVG_FONTS
842 /*
843 Local Variables:
844 mode:c++
845 c-file-style:"stroustrup"
846 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
847 indent-tabs-mode:nil
848 fill-column:99
849 End:
850 */
851 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :