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