diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2013-03-03 22:17:21 +0000 |
|---|---|---|
| committer | Jabiertxo Arraiza Zenotz <jtx@jtx.marker.es> | 2013-03-03 22:17:21 +0000 |
| commit | f5f95da9b90081aadaf4f939a5fd8512ecd941b7 (patch) | |
| tree | 9be295ce3ab7727c41a1fe00ce00317b8d9ebc8c /src | |
| parent | BSpline refactor (diff) | |
| parent | Update from branch (diff) | |
| download | inkscape-f5f95da9b90081aadaf4f939a5fd8512ecd941b7.tar.gz inkscape-f5f95da9b90081aadaf4f939a5fd8512ecd941b7.zip | |
Spiro refactor
(bzr r11950.1.42)
Diffstat (limited to 'src')
30 files changed, 1652 insertions, 868 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 65baec21b..a899c7b35 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,6 +37,7 @@ noinst_LIBRARIES = \ all_libs = \ $(noinst_LIBRARIES) \ $(INKSCAPE_LIBS) \ + $(EXIF_LIBS) \ $(GNOME_VFS_LIBS) \ $(XFT_LIBS) \ $(FREETYPE_LIBS) \ @@ -66,6 +67,7 @@ EXTRA_DIST = INCLUDES = \ $(PERL_CFLAGS) $(PYTHON_CFLAGS) \ + $(EXIF_CFLAGS) \ $(FREETYPE_CFLAGS) \ $(GNOME_PRINT_CFLAGS) \ $(GNOME_VFS_CFLAGS) \ diff --git a/src/device-manager.cpp b/src/device-manager.cpp index a9394a5f6..a07231805 100644 --- a/src/device-manager.cpp +++ b/src/device-manager.cpp @@ -14,6 +14,7 @@ #include "device-manager.h" #include "preferences.h" #include <gtk/gtk.h> +#include <glibmm/regex.h> #define noDEBUG_VERBOSE 1 diff --git a/src/extension/CMakeLists.txt b/src/extension/CMakeLists.txt index b5634f42f..fa4fdd740 100644 --- a/src/extension/CMakeLists.txt +++ b/src/extension/CMakeLists.txt @@ -36,6 +36,7 @@ set(extension_SRC internal/cairo-render-context.cpp internal/cairo-renderer.cpp internal/cairo-renderer-pdf-out.cpp + internal/cdr-input.cpp internal/emf-win32-inout.cpp internal/emf-win32-print.cpp internal/gdkpixbuf-input.cpp @@ -100,6 +101,7 @@ set(extension_SRC internal/cairo-render-context.h internal/cairo-renderer-pdf-out.h internal/cairo-renderer.h + internal/cdr-input.h internal/clear-n_.h internal/emf-win32-inout.h internal/emf-win32-print.h diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index 4af778e04..fa46569e2 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -23,6 +23,8 @@ #include <gtkmm/main.h> #include <gtkmm/scrolledwindow.h> #include <gtkmm/textview.h> +#include <glibmm/miscutils.h> +#include <glibmm/convert.h> #include <unistd.h> #include <errno.h> diff --git a/src/extension/implementation/script.h b/src/extension/implementation/script.h index f52683623..270c361af 100644 --- a/src/extension/implementation/script.h +++ b/src/extension/implementation/script.h @@ -15,6 +15,9 @@ #include "implementation.h" #include <gtkmm/enums.h> +#include <glibmm/main.h> +#include <glibmm/spawn.h> +#include <glibmm/fileutils.h> namespace Inkscape { namespace XML { diff --git a/src/extension/system.cpp b/src/extension/system.cpp index a9ca5c456..56cc6d1af 100644 --- a/src/extension/system.cpp +++ b/src/extension/system.cpp @@ -22,6 +22,7 @@ #include <interface.h> #include <unistd.h> +#include <glibmm/miscutils.h> #include "system.h" #include "preferences.h" diff --git a/src/ink-comboboxentry-action.cpp b/src/ink-comboboxentry-action.cpp index fd146926f..320472347 100644 --- a/src/ink-comboboxentry-action.cpp +++ b/src/ink-comboboxentry-action.cpp @@ -303,11 +303,18 @@ ink_comboboxentry_action_class_init (Ink_ComboBoxEntry_ActionClass *klass) static void ink_comboboxentry_action_init (Ink_ComboBoxEntry_Action *action) { action->active = -1; - action->text = NULL; + action->text = strdup(""); action->entry_completion = NULL; action->indicator = NULL; action->popup = false; + action->info = NULL; + action->info_cb = NULL; + action->info_cb_id = 0; + action->info_cb_blocked = false; action->warning = NULL; + action->warning_cb = NULL; + action->warning_cb_id = 0; + action->warning_cb_blocked = false; action->altx_name = NULL; action->focusWidget = NULL; } @@ -370,7 +377,8 @@ GtkWidget* create_tool_item( GtkAction* action ) ink_comboboxentry_action->combobox = GTK_COMBO_BOX (comboBoxEntry); - gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), ink_comboboxentry_action->active ); + //gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), ink_comboboxentry_action->active ); + gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), 0 ); g_signal_connect( G_OBJECT(comboBoxEntry), "changed", G_CALLBACK(combo_box_changed_cb), action ); @@ -432,7 +440,6 @@ GtkWidget* create_tool_item( GtkAction* action ) // Add signal for GtkEntry to check if finished typing. g_signal_connect( G_OBJECT(child), "activate", G_CALLBACK(entry_activate_cb), action ); g_signal_connect( G_OBJECT(child), "key-press-event", G_CALLBACK(keypress_cb), action ); - } gtk_activatable_set_related_action( GTK_ACTIVATABLE (item), GTK_ACTION( action ) ); @@ -476,66 +483,141 @@ gchar* ink_comboboxentry_action_get_active_text( Ink_ComboBoxEntry_Action* actio return text; } -gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* ink_comboboxentry_action, const gchar* text ) { +/* + * For the font-family list we need to handle two cases: + * Text is in list store: + * In this case we use row number as the font-family list can have duplicate + * entries, one in the document font part and one in the system font part. In + * order that scrolling through the list works properly we must distinguish + * between the two. + * Text is not in the list store (i.e. default font-family is not on system): + * In this case we have a row number of -1, and the text must be set by hand. + */ +gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* action, const gchar* text, int row ) { - g_free( ink_comboboxentry_action->text ); - ink_comboboxentry_action->text = g_strdup( text ); + if( strcmp( action->text, text ) != 0 ) { + g_free( action->text ); + action->text = g_strdup( text ); + } // Get active row or -1 if none - ink_comboboxentry_action->active = get_active_row_from_text( ink_comboboxentry_action, ink_comboboxentry_action->text ); + if( row < 0 ) { + row = get_active_row_from_text( action, action->text ); + } + action->active = row; // Set active row, check that combobox has been created. - if( ink_comboboxentry_action->combobox ) { - gtk_combo_box_set_active( GTK_COMBO_BOX( ink_comboboxentry_action->combobox ), ink_comboboxentry_action->active ); + if( action->combobox ) { + gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox ), action->active ); } // Fiddle with entry - if( ink_comboboxentry_action->entry ) { + if( action->entry ) { // Explicitly set text in GtkEntry box (won't be set if text not in list). - gtk_entry_set_text( ink_comboboxentry_action->entry, text ); + gtk_entry_set_text( action->entry, text ); // Show or hide warning -- this might be better moved to text-toolbox.cpp - bool clear = true; + if( action->info_cb_id != 0 && + !action->info_cb_blocked ) { + g_signal_handler_block (G_OBJECT(action->entry), + action->info_cb_id ); + action->info_cb_blocked = true; + } + if( action->warning_cb_id != 0 && + !action->warning_cb_blocked ) { + g_signal_handler_block (G_OBJECT(action->entry), + action->warning_cb_id ); + action->warning_cb_blocked = true; + } - if( ink_comboboxentry_action->warning != NULL ) { - Glib::ustring missing = check_comma_separated_text( ink_comboboxentry_action ); + bool set = false; + if( action->warning != NULL ) { + Glib::ustring missing = check_comma_separated_text( action ); if( !missing.empty() ) { - GtkStockItem item; - gboolean isStock = gtk_stock_lookup( GTK_STOCK_DIALOG_WARNING, &item ); - if (isStock) { - gtk_entry_set_icon_from_stock( ink_comboboxentry_action->entry, + GtkStockItem item; + gboolean isStock = gtk_stock_lookup( GTK_STOCK_DIALOG_WARNING, &item ); + if (isStock) { + gtk_entry_set_icon_from_stock( action->entry, + GTK_ENTRY_ICON_SECONDARY, + GTK_STOCK_DIALOG_WARNING ); + } else { + gtk_entry_set_icon_from_icon_name( action->entry, GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_WARNING ); - } else { - gtk_entry_set_icon_from_icon_name( ink_comboboxentry_action->entry, - GTK_ENTRY_ICON_SECONDARY, - GTK_STOCK_DIALOG_WARNING ); + } + // Can't add tooltip until icon set + Glib::ustring warning = action->warning; + warning += ": "; + warning += missing; + gtk_entry_set_icon_tooltip_text( action->entry, + GTK_ENTRY_ICON_SECONDARY, + warning.c_str() ); + + if( action->warning_cb ) { + + // Add callback if we haven't already + if( action->warning_cb_id == 0 ) { + action->warning_cb_id = + g_signal_connect( G_OBJECT(action->entry), + "icon-press", + G_CALLBACK(action->warning_cb), + action); } - // Can't add tooltip until icon set - Glib::ustring warning = ink_comboboxentry_action->warning; - warning += ": "; - warning += missing; - gtk_entry_set_icon_tooltip_text( ink_comboboxentry_action->entry, - GTK_ENTRY_ICON_SECONDARY, - warning.c_str() ); - clear = false; + // Unblock signal + if( action->warning_cb_blocked ) { + g_signal_handler_unblock (G_OBJECT(action->entry), + action->warning_cb_id ); + action->warning_cb_blocked = false; + } + } + set = true; } } - if( clear ) { - gtk_entry_set_icon_from_icon_name( GTK_ENTRY(ink_comboboxentry_action->entry), + if( !set && action->info != NULL ) { + gtk_entry_set_icon_from_icon_name( GTK_ENTRY(action->entry), + GTK_ENTRY_ICON_SECONDARY, + GTK_STOCK_SELECT_ALL ); + gtk_entry_set_icon_from_stock( GTK_ENTRY(action->entry), + GTK_ENTRY_ICON_SECONDARY, + GTK_STOCK_SELECT_ALL ); + gtk_entry_set_icon_tooltip_text( action->entry, + GTK_ENTRY_ICON_SECONDARY, + action->info ); + + if( action->info_cb ) { + // Add callback if we haven't already + if( action->info_cb_id == 0 ) { + action->info_cb_id = + g_signal_connect( G_OBJECT(action->entry), + "icon-press", + G_CALLBACK(action->info_cb), + action); + } + // Unblock signal + if( action->info_cb_blocked ) { + g_signal_handler_unblock (G_OBJECT(action->entry), + action->info_cb_id ); + action->info_cb_blocked = false; + } + } + set = true; + } + + if( !set ) { + gtk_entry_set_icon_from_icon_name( GTK_ENTRY(action->entry), GTK_ENTRY_ICON_SECONDARY, NULL ); - gtk_entry_set_icon_from_stock( GTK_ENTRY(ink_comboboxentry_action->entry), + gtk_entry_set_icon_from_stock( GTK_ENTRY(action->entry), GTK_ENTRY_ICON_SECONDARY, NULL ); } } // Return if active text in list - gboolean found = ( ink_comboboxentry_action->active != -1 ); + gboolean found = ( action->active != -1 ); return found; } @@ -611,6 +693,24 @@ void ink_comboboxentry_action_set_tooltip( Ink_ComboBoxEntry_Action* action, } +void ink_comboboxentry_action_set_info( Ink_ComboBoxEntry_Action* action, const gchar* info ) { + + g_free( action->info ); + action->info = g_strdup( info ); + + // Widget may not have been created.... + if( action->entry ) { + gtk_entry_set_icon_tooltip_text( GTK_ENTRY(action->entry), + GTK_ENTRY_ICON_SECONDARY, + action->info ); + } +} + +void ink_comboboxentry_action_set_info_cb( Ink_ComboBoxEntry_Action* action, gpointer info_cb ) { + + action->info_cb = info_cb; +} + void ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, const gchar* warning ) { g_free( action->warning ); @@ -624,6 +724,11 @@ void ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, } } +void ink_comboboxentry_action_set_warning_cb( Ink_ComboBoxEntry_Action* action, gpointer warning_cb ) { + + action->warning_cb = warning_cb; +} + void ink_comboboxentry_action_set_altx_name( Ink_ComboBoxEntry_Action* action, const gchar* altx_name ) { g_free( action->altx_name ); @@ -728,22 +833,27 @@ static void combo_box_changed_cb( GtkComboBox* widget, gpointer data ) { // We only react here if an item is selected. // Get action - Ink_ComboBoxEntry_Action *act = INK_COMBOBOXENTRY_ACTION( data ); + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); // Check if item selected: gint newActive = gtk_combo_box_get_active(widget); - if( newActive >= 0 ) { + if( newActive >= 0 && newActive != action->active ) { - if( newActive != act->active ) { - act->active = newActive; - g_free( act->text ); - GtkWidget *entry = gtk_bin_get_child (GTK_BIN (widget)); - act->text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + action->active = newActive; - // Now let the world know - g_signal_emit( G_OBJECT(act), signals[CHANGED], 0 ); + GtkTreeIter iter; + if( gtk_combo_box_get_active_iter( GTK_COMBO_BOX( action->combobox ), &iter ) ) { + gchar* text = 0; + gtk_tree_model_get( action->model, &iter, 0, &text, -1 ); + gtk_entry_set_text( action->entry, text ); + + g_free( action->text ); + action->text = text; } + + // Now let the world know + g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); } } @@ -752,29 +862,29 @@ static void entry_activate_cb( GtkEntry* widget, gpointer data ) { // Get text from entry box.. check if it matches a menu entry. // Get action - Ink_ComboBoxEntry_Action *ink_comboboxentry_action = INK_COMBOBOXENTRY_ACTION( data ); + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); // Get text - g_free( ink_comboboxentry_action->text ); - ink_comboboxentry_action->text = g_strdup( gtk_entry_get_text( widget ) ); + g_free( action->text ); + action->text = g_strdup( gtk_entry_get_text( widget ) ); // Get row - ink_comboboxentry_action->active = - get_active_row_from_text( ink_comboboxentry_action, ink_comboboxentry_action->text ); + action->active = + get_active_row_from_text( action, action->text ); // Set active row - gtk_combo_box_set_active( GTK_COMBO_BOX( ink_comboboxentry_action->combobox), ink_comboboxentry_action->active ); + gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox), action->active ); // Now let the world know - g_signal_emit( G_OBJECT(ink_comboboxentry_action), signals[CHANGED], 0 ); + g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); } static gboolean match_selected_cb( GtkEntryCompletion* /*widget*/, GtkTreeModel* model, GtkTreeIter* iter, gpointer data ) { // Get action - Ink_ComboBoxEntry_Action *ink_comboboxentry_action = INK_COMBOBOXENTRY_ACTION( data ); - GtkEntry *entry = ink_comboboxentry_action->entry; + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); + GtkEntry *entry = action->entry; if( entry) { gchar *family = 0; @@ -784,18 +894,18 @@ static gboolean match_selected_cb( GtkEntryCompletion* /*widget*/, GtkTreeModel* gtk_entry_set_text (GTK_ENTRY (entry), family ); // Set text in GtkAction - g_free( ink_comboboxentry_action->text ); - ink_comboboxentry_action->text = family; + g_free( action->text ); + action->text = family; // Get row - ink_comboboxentry_action->active = - get_active_row_from_text( ink_comboboxentry_action, ink_comboboxentry_action->text ); + action->active = + get_active_row_from_text( action, action->text ); // Set active row - gtk_combo_box_set_active( GTK_COMBO_BOX( ink_comboboxentry_action->combobox), ink_comboboxentry_action->active ); + gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox), action->active ); // Now let the world know - g_signal_emit( G_OBJECT(ink_comboboxentry_action), signals[CHANGED], 0 ); + g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); return true; } diff --git a/src/ink-comboboxentry-action.h b/src/ink-comboboxentry-action.h index f0dc0ee7e..a66f0790e 100644 --- a/src/ink-comboboxentry-action.h +++ b/src/ink-comboboxentry-action.h @@ -58,7 +58,14 @@ struct _Ink_ComboBoxEntry_Action { gint entry_width;// Width of GtkEntry in characters. gint extra_width;// Extra Width of GtkComboBox.. to widen drop-down list in list mode. gboolean popup; // Do we pop-up an entry-completion dialog? - gchar *warning; // Text for warning that entry isn't in list. + gchar *info; // Text for tooltip info about entry. + gpointer info_cb; // Callback for clicking info icon. + gint info_cb_id; + gboolean info_cb_blocked; + gchar *warning; // Text for tooltip warning that entry isn't in list. + gpointer warning_cb; // Callback for clicking warning icon. + gint warning_cb_id; + gboolean warning_cb_blocked; gchar *altx_name; // Target for Alt-X keyboard shortcut. GtkWidget *focusWidget; }; @@ -84,7 +91,7 @@ GtkTreeModel *ink_comboboxentry_action_get_model( Ink_ComboBoxEntry_Action* GtkComboBox *ink_comboboxentry_action_get_comboboxentry( Ink_ComboBoxEntry_Action* action ); gchar* ink_comboboxentry_action_get_active_text( Ink_ComboBoxEntry_Action* action ); -gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* action, const gchar* text ); +gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* action, const gchar* text, int row=-1 ); void ink_comboboxentry_action_set_entry_width( Ink_ComboBoxEntry_Action* action, gint entry_width ); void ink_comboboxentry_action_set_extra_width( Ink_ComboBoxEntry_Action* action, gint extra_width ); @@ -92,8 +99,11 @@ void ink_comboboxentry_action_set_extra_width( Ink_ComboBoxEntry_Action* act void ink_comboboxentry_action_popup_enable( Ink_ComboBoxEntry_Action* action ); void ink_comboboxentry_action_popup_disable( Ink_ComboBoxEntry_Action* action ); -void ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, const gchar* warning ); -void ink_comboboxentry_action_set_tooltip( Ink_ComboBoxEntry_Action* action, const gchar* tooltip ); +void ink_comboboxentry_action_set_info( Ink_ComboBoxEntry_Action* action, const gchar* info ); +void ink_comboboxentry_action_set_info_cb( Ink_ComboBoxEntry_Action* action, gpointer info_cb ); +void ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, const gchar* warning_cb ); +void ink_comboboxentry_action_set_warning_cb(Ink_ComboBoxEntry_Action* action, gpointer warning ); +void ink_comboboxentry_action_set_tooltip( Ink_ComboBoxEntry_Action* action, const gchar* tooltip ); void ink_comboboxentry_action_set_altx_name( Ink_ComboBoxEntry_Action* action, const gchar* altx_name ); diff --git a/src/inkscape.cpp b/src/inkscape.cpp index fc823f8b7..449220357 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -37,6 +37,8 @@ #include <glib/gstdio.h> #include <glib.h> #include <glibmm/i18n.h> +#include <glibmm/miscutils.h> +#include <glibmm/convert.h> #include <gtkmm/messagedialog.h> #include <gtk/gtk.h> #include <signal.h> diff --git a/src/interface.cpp b/src/interface.cpp index 823119953..284368bff 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -458,47 +458,21 @@ static GtkWidget *sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb item = gtk_separator_menu_item_new(); } else { - unsigned int shortcut; action = verb->get_action(view); if (!action) return NULL; - shortcut = sp_shortcut_get_primary(verb); - if (shortcut!=GDK_KEY_VoidSymbol) { - gchar* c = sp_shortcut_get_label(shortcut); -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget *const hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16); - gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); -#else - GtkWidget *const hb = gtk_hbox_new(FALSE, 16); -#endif - GtkWidget *const name_lbl = gtk_label_new(""); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name); - gtk_misc_set_alignment(reinterpret_cast<GtkMisc *>(name_lbl), 0.0, 0.5); - gtk_box_pack_start(reinterpret_cast<GtkBox *>(hb), name_lbl, TRUE, TRUE, 0); - GtkWidget *const accel_lbl = gtk_label_new(c); - gtk_misc_set_alignment(reinterpret_cast<GtkMisc *>(accel_lbl), 1.0, 0.5); - gtk_box_pack_end(reinterpret_cast<GtkBox *>(hb), accel_lbl, FALSE, FALSE, 0); - gtk_widget_show_all(hb); - if (radio) { - item = gtk_radio_menu_item_new (group); - } else { - item = gtk_image_menu_item_new(); - } - gtk_container_add(reinterpret_cast<GtkContainer *>(item), hb); - g_free(c); + if (radio) { + item = gtk_radio_menu_item_new_with_mnemonic(group, action->name); } else { - if (radio) { - item = gtk_radio_menu_item_new (group); - } else { - item = gtk_image_menu_item_new (); - } - GtkWidget *const name_lbl = gtk_label_new(""); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name); - gtk_misc_set_alignment(reinterpret_cast<GtkMisc *>(name_lbl), 0.0, 0.5); - gtk_container_add(reinterpret_cast<GtkContainer *>(item), name_lbl); + item = gtk_image_menu_item_new_with_mnemonic(action->name); } + GtkAccelGroup *accel_group = sp_shortcut_get_accel_group(); + gtk_menu_set_accel_group(menu, accel_group); + + sp_shortcut_add_accelerator(item, sp_shortcut_get_primary(verb)); + action->signal_set_sensitive.connect( sigc::bind<0>( sigc::ptr_fun(>k_widget_set_sensitive), @@ -698,45 +672,16 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View * { unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0; SPAction *action = (verb) ? verb->get_action(view) : 0; - GtkWidget *item = gtk_check_menu_item_new(); - - - if (verb && shortcut!=GDK_KEY_VoidSymbol) { - gchar* c = sp_shortcut_get_label(shortcut); - -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget *hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16); - gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); -#else - GtkWidget *hb = gtk_hbox_new(FALSE, 16); -#endif + GtkWidget *item = gtk_check_menu_item_new_with_mnemonic(action ? action->name : label); - { - GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label); - gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5); - gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0); - } - - { - GtkWidget *l = gtk_label_new(c); - gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5); - gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0); - } - - gtk_widget_show_all(hb); - - gtk_container_add((GtkContainer *) item, hb); - g_free(c); - } else { - GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label); - gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5); - gtk_container_add((GtkContainer *) item, l); - } #if 0 if (!action->sensitive) { gtk_widget_set_sensitive(item, FALSE); } #endif + + sp_shortcut_add_accelerator(item, shortcut); + gtk_widget_show(item); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); @@ -1726,40 +1671,13 @@ void ContextMenu::AppendItemFromVerb(Inkscape::Verb *verb)//, SPDesktop *view)// append(*item); } else { action = verb->get_action(view); - if (!action) - { + if (!action) { return; } - Gtk::ImageMenuItem *item = NULL; - unsigned int shortcut = sp_shortcut_get_primary(verb); - if (shortcut!=GDK_KEY_VoidSymbol) { - gchar* c = sp_shortcut_get_label(shortcut); - Gtk::HBox *const hb = manage(new Gtk::HBox (FALSE, 16)); - Gtk::Label *const name_lbl = manage(new Gtk::Label(action->name, true)); - name_lbl->set_alignment(0.0, 0.5); - hb->pack_start(*name_lbl, TRUE, TRUE, 0); - Gtk::Label *const accel_lbl = manage(new Gtk::Label(c)); - accel_lbl->set_alignment(1.0, 0.5); - hb->pack_end(*accel_lbl, FALSE, FALSE, 0); - hb->show_all(); - // if (radio) { - // item = gtk_radio_menu_item_new (group); - // } else { - item = new Gtk::ImageMenuItem(); - // } - item->add(*hb); - g_free(c); - } else { - // if (radio) { - // item = gtk_radio_menu_item_new (group); - // } else { - item = manage(new Gtk::ImageMenuItem()); - // } - Gtk::Label *const name_lbl = manage(new Gtk::Label(action->name, true)); - name_lbl->set_alignment(0.0, 0.5); - item->add(*name_lbl); - } + Gtk::ImageMenuItem *item = manage(new Gtk::ImageMenuItem(action->name, true)); + + sp_shortcut_add_accelerator(GTK_WIDGET(item->gobj()), sp_shortcut_get_primary(verb)); action->signal_set_sensitive.connect(sigc::mem_fun(*this, &ContextMenu::set_sensitive)); action->signal_set_name.connect(sigc::mem_fun(*item, &ContextMenu::set_name)); diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp index 04859185c..5e67c5991 100644 --- a/src/libnrtype/font-lister.cpp +++ b/src/libnrtype/font-lister.cpp @@ -10,15 +10,19 @@ #include <glibmm.h> #include <gtkmm/treemodel.h> #include <gtkmm/liststore.h> - #include "font-lister.h" #include "FontFactory.h" +#include "desktop.h" +#include "desktop-style.h" +#include "document.h" +#include "inkscape.h" +#include "preferences.h" #include "sp-object.h" #include "sp-root.h" -#include "document.h" #include "xml/repr.h" -#include "preferences.h" + +//#define DEBUG_FONT namespace Inkscape { @@ -29,7 +33,7 @@ namespace Inkscape FamilyToStylesMap familyStyleMap; font_factory::Default()->GetUIFamiliesAndStyles(&familyStyleMap); - + // Grab the family names into a list and then sort them std::list<Glib::ustring> familyList; for (FamilyToStylesMap::iterator iter = familyStyleMap.begin(); @@ -47,7 +51,8 @@ namespace Inkscape if (!familyName.empty()) { Gtk::TreeModel::iterator treeModelIter = font_list_store->append(); - (*treeModelIter)[FontList.font] = reinterpret_cast<const char*>(g_strdup(familyName.c_str())); + //(*treeModelIter)[FontList.family] = reinterpret_cast<const char*>(g_strdup(familyName.c_str())); + (*treeModelIter)[FontList.family] = familyName; // Now go through the styles GList *styles = NULL; @@ -62,7 +67,22 @@ namespace Inkscape (*treeModelIter)[FontList.onSystem] = true; } } + current_family_row = 0; + current_family = "sans-serif"; + current_style = "Normal"; + current_fontspec = "sans-serif"; // Empty style -> Normal + current_fontspec_system = "Sans"; + + /* Create default styles for use when font-family is unknown on system. */ + default_styles = g_list_append( NULL, g_strdup("Normal") ); + default_styles = g_list_append( default_styles, g_strdup("Italic") ); + default_styles = g_list_append( default_styles, g_strdup("Bold") ); + default_styles = g_list_append( default_styles, g_strdup("Bold Italic") ); + font_list_store->thaw_notify(); + + style_list_store = Gtk::ListStore::create (FontStyleList); + style_list_store_trial = Gtk::ListStore::create (FontStyleList); } // Example of how to use "foreach_iter" @@ -70,7 +90,7 @@ namespace Inkscape // FontLister::print_document_font( const Gtk::TreeModel::iterator &iter ) { // Gtk::TreeModel::Row row = *iter; // if( !row[FontList.onSystem] ) { - // std::cout << " Not on system: " << row[FontList.font] << std::endl; + // std::cout << " Not on system: " << row[FontList.family] << std::endl; // return false; // } // return true; @@ -87,28 +107,31 @@ namespace Inkscape font_list_store->freeze_notify(); + /* Find if current row is in document or system part of list */ + gboolean row_is_system = false; + if( current_family_row > -1 ) { + Gtk::TreePath path; + path.push_back( current_family_row ); + Gtk::TreeModel::iterator iter = font_list_store->get_iter( path ); + if( iter ) { + row_is_system = (*iter)[FontList.onSystem]; + // std::cout << " In: row: " << current_family_row << " " << (*iter)[FontList.family] << std::endl; + } + } + /* Clear all old document font-family entries */ Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" ); while( iter != font_list_store->children().end() ) { Gtk::TreeModel::Row row = *iter; if( !row[FontList.onSystem] ) { - // std::cout << " Not on system: " << row[FontList.font] << std::endl; + // std::cout << " Not on system: " << row[FontList.family] << std::endl; iter = font_list_store->erase( iter ); } else { - // std::cout << " First on system: " << row[FontList.font] << std::endl; + // std::cout << " First on system: " << row[FontList.family] << std::endl; break; } } - /* Create default styles for use when font-family is unknown on system. */ - static GList *default_styles = NULL; - if( default_styles == NULL ) { - default_styles = g_list_append( default_styles, g_strdup("Normal") ); - default_styles = g_list_append( default_styles, g_strdup("Italic") ); - default_styles = g_list_append( default_styles, g_strdup("Bold") ); - default_styles = g_list_append( default_styles, g_strdup("Bold Italic") ); - } - /* Get "font-family"s used in document. */ std::list<Glib::ustring> fontfamilies; update_font_list_recursive( r, &fontfamilies ); @@ -120,7 +143,7 @@ namespace Inkscape /* Insert separator */ if( !fontfamilies.empty() ) { Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); - (*treeModelIter)[FontList.font] = "#"; + (*treeModelIter)[FontList.family] = "#"; (*treeModelIter)[FontList.onSystem] = false; } @@ -137,7 +160,7 @@ namespace Inkscape Gtk::TreeModel::iterator iter2 = font_list_store->get_iter( "0" ); while( iter2 != font_list_store->children().end() ) { Gtk::TreeModel::Row row = *iter2; - if( row[FontList.onSystem] && tokens[0].compare( row[FontList.font] ) == 0 ) { + if( row[FontList.onSystem] && tokens[0].compare( row[FontList.family] ) == 0 ) { styles = row[FontList.styles]; break; } @@ -146,11 +169,37 @@ namespace Inkscape } Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); - (*treeModelIter)[FontList.font] = reinterpret_cast<const char*>(g_strdup((*i).c_str())); + (*treeModelIter)[FontList.family] = reinterpret_cast<const char*>(g_strdup((*i).c_str())); (*treeModelIter)[FontList.styles] = styles; (*treeModelIter)[FontList.onSystem] = false; } + /* Now we do a song and dance to find the correct row as the row corresponding + * to the current_family may have changed. We can't simply search for the + * family name in the list since it can occur twice, once in the document + * font family part and once in the system font family part. Above we determined + * which part it is in. + */ + if( current_family_row > -1 ) { + int start = 0; + if( row_is_system ) start = fontfamilies.size(); + int length = font_list_store->children().size(); + for( int i = 0; i < length; ++i ) { + int row = i + start; + if( row >= length ) row -= length; + Gtk::TreePath path; + path.push_back( row ); + Gtk::TreeModel::iterator iter = font_list_store->get_iter( path ); + if( iter ) { + if( current_family.compare( (*iter)[FontList.family] ) == 0 ) { + current_family_row = row; + break; + } + } + } + } + // std::cout << " Out: row: " << current_family_row << " " << current_family << std::endl; + font_list_store->thaw_notify(); } @@ -191,7 +240,578 @@ namespace Inkscape } } - Gtk::TreePath + Glib::ustring + FontLister::canonize_fontspec( Glib::ustring fontspec ) { + + // Pass fontspec to and back from Pango to get a the fontspec in + // canonical form. -inkscape-font-specification relies on the + // Pango constructed fontspec not changing form. If it does, + // this is the place to fix it. + PangoFontDescription *descr = pango_font_description_from_string( fontspec.c_str() ); + gchar* canonized = pango_font_description_to_string ( descr ); + Glib::ustring Canonized = canonized; + g_free( canonized ); + pango_font_description_free( descr ); + + // Pango canonized strings remove space after comma between family names. Put it back. + size_t i = 0; + while( (i = Canonized.find(",", i)) != std::string::npos) { + Canonized.replace(i, 1, ", "); + i += 2; + } + + return Canonized; + } + + Glib::ustring + FontLister::system_fontspec( Glib::ustring fontspec ) { + + // Find what Pango thinks is the closest match. + Glib::ustring out = fontspec; + + PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); + font_instance *res = (font_factory::Default())->Face(descr); + if (res->pFont) { + PangoFontDescription *nFaceDesc = pango_font_describe(res->pFont); + out = sp_font_description_get_family(nFaceDesc); + } + pango_font_description_free(descr); + + return out; + } + + std::pair<Glib::ustring, Glib::ustring> + FontLister::ui_from_fontspec( Glib::ustring fontspec ) { + + PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); + const gchar* family = pango_font_description_get_family(descr); + Glib::ustring Family = family; + + // Pango canonized strings remove space after comma between family names. Put it back. + size_t i = 0; + while( (i = Family.find(",", i)) != std::string::npos) { + Family.replace(i, 1, ", "); + i += 2; + } + + pango_font_description_unset_fields(descr, PANGO_FONT_MASK_FAMILY); + gchar* style = pango_font_description_to_string( descr ); + Glib::ustring Style = style; + pango_font_description_free(descr); + g_free( style ); + + return std::make_pair( Family, Style ); + } + + std::pair<Glib::ustring, Glib::ustring> + FontLister::selection_update () { + +#ifdef DEBUG_FONT + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::selection_update: entrance" << std::endl; +#endif + // Get fontspec from a selection, preferences, or thin air. + Glib::ustring fontspec; + SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); + + // Directly from stored font specification. + int result = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); + + //std::cout << " Attempting selected style" << std::endl; + if( result != QUERY_STYLE_NOTHING && query->text->font_specification.set ) { + fontspec = query->text->font_specification.value; + //std::cout << " fontspec from query :" << fontspec << ":" << std::endl; + } + + // From style + if( fontspec.empty() ) { + //std::cout << " Attempting desktop style" << std::endl; + int rfamily = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); + int rstyle = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); + + // Must have text in selection + if( rfamily != QUERY_STYLE_NOTHING && rstyle != QUERY_STYLE_NOTHING ) { + fontspec = fontspec_from_style( query ); + } + //std::cout << " fontspec from style :" << fontspec << ":" << std::endl; + } + + // From preferences + if( fontspec.empty() ) { + //std::cout << " Attempting preferences" << std::endl; + sp_style_read_from_prefs(query, "/tools/text"); + fontspec = fontspec_from_style( query ); + //std::cout << " fontspec from prefs :" << fontspec << ":" << std::endl; + } + sp_style_unref(query); + + // From thin air + if( fontspec.empty() ) { + //std::cout << " Attempting thin air" << std::endl; + fontspec = current_family + ", " + current_style; + //std::cout << " fontspec from thin air :" << fontspec << ":" << std::endl; + } + + // Do we really need? Removes spaces between font-families. + //current_fontspec = canonize_fontspec( fontspec ); + current_fontspec = fontspec; // Ignore for now + + current_fontspec_system = system_fontspec( current_fontspec ); + + std::pair<Glib::ustring, Glib::ustring> ui = ui_from_fontspec( current_fontspec ); + set_font_family( ui.first ); + +#ifdef DEBUG_FONT + std::cout << " family_row: :" << current_family_row << ":" << std::endl; + std::cout << " canonized: :" << current_fontspec << ":" << std::endl; + std::cout << " system: :" << current_fontspec_system << ":" << std::endl; + std::cout << " family: :" << current_family << ":" << std::endl; + std::cout << " style: :" << current_style << ":" << std::endl; + std::cout << "FontLister::selection_update: exit" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; +#endif + return std::make_pair( current_family, current_style ); + } + + + // TODO: use to determine font-selector best style + std::pair<Glib::ustring, Glib::ustring> + FontLister::new_font_family (Glib::ustring new_family, gboolean check_style ) { + +#ifdef DEBUG_FONT + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::new_font_family: " << new_family << std::endl; +#endif + + // No need to do anything if new family is same as old family. + if( new_family.compare( current_family ) == 0 ) { +#ifdef DEBUG_FONT + std::cout << "FontLister::new_font_family: exit: no change in family." << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; +#endif + return std::make_pair( current_family, current_style ); + } + + // We need to do two things: + // 1. Update style list for new family. + // 2. Select best valid style match to old style. + + // For finding style list, use list of first family in font-family list. + GList* styles = NULL; + Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" ); + while( iter != font_list_store->children().end() ) { + + Gtk::TreeModel::Row row = *iter; + + if( new_family.compare( row[FontList.family] ) == 0 ) { + styles = row[FontList.styles]; + break; + } + ++iter; + } + + // Newly typed in font-family may not yet be in list... use default list. + // TODO: if font-family is list, check if first family in list is on system + // and set style accordingly. + if( styles == NULL ) { + styles = default_styles; + } + + // Update style list. + // TODO: create a second "temporary" style_list_store for font_selector. + style_list_store->freeze_notify(); + style_list_store->clear(); + + for (GList *l=styles; l; l = l->next) { + Gtk::TreeModel::iterator treeModelIter = style_list_store->append(); + (*treeModelIter)[FontStyleList.styles] = (char*)l->data; + } + + style_list_store->thaw_notify(); + + // Find best match to the style from the old font-family to the + // styles available with the new font. + // TODO: Maybe check if an exact match exists before using Pango. + Glib::ustring best_style = current_style; + if( check_style ) { + //std::cout << " Trying to match: " << current_fontspec << std::endl; + PangoFontDescription *desc_old + = pango_font_description_from_string( current_fontspec.c_str() ); + PangoFontDescription* desc_best = NULL; + + for (GList *l=styles; l; l = l->next) { + Glib::ustring candidate = new_family + ", " + (char*)l->data; + PangoFontDescription* desc_candidate + = pango_font_description_from_string( candidate.c_str() ); + //std::cout << " Testing: " << pango_font_description_to_string( desc_candidate ) << std::endl; + if( pango_font_description_better_match( desc_old, desc_best, desc_candidate ) ) { + pango_font_description_free( desc_best ); + desc_best = desc_candidate; + //std::cout << " ... better: " << std::endl; + } else { + pango_font_description_free( desc_candidate ); + //std::cout << " ... not better: " << std::endl; + } + } + if( desc_best ) { + pango_font_description_unset_fields( desc_best, PANGO_FONT_MASK_FAMILY ); + best_style = pango_font_description_to_string( desc_best ); + } + + if( desc_old ) pango_font_description_free( desc_old ); + if( desc_best ) pango_font_description_free( desc_best ); + } + +#ifdef DEBUG_FONT + std::cout << "FontLister::new_font_family: exit: " << new_family << " " << best_style << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; +#endif + return std::make_pair( new_family, best_style ); + } + + std::pair<Glib::ustring, Glib::ustring> + FontLister::set_font_family (Glib::ustring new_family, gboolean check_style) { + +#ifdef DEBUG_FONT + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::set_font_family: " << new_family << std::endl; +#endif + + std::pair<Glib::ustring, Glib::ustring> ui = new_font_family( new_family, check_style ); + current_family = ui.first; + current_style = ui.second; + current_fontspec = canonize_fontspec( current_family + ", " + current_style ); + current_fontspec_system = system_fontspec( current_fontspec ); + +#ifdef DEBUG_FONT + std::cout << " family_row: :" << current_family_row << ":" << std::endl; + std::cout << " canonized: :" << current_fontspec << ":" << std::endl; + std::cout << " system: :" << current_fontspec_system << ":" << std::endl; + std::cout << " family: :" << current_family << ":" << std::endl; + std::cout << " style: :" << current_style << ":" << std::endl; + std::cout << "FontLister::set_font_family: end" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; +#endif + return ui; + } + + + std::pair<Glib::ustring, Glib::ustring> + FontLister::set_font_family (int row, gboolean check_style) { + +#ifdef DEBUG_FONT + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::set_font_family( row ): " << row << std::endl; +#endif + + current_family_row = row; + Gtk::TreePath path; + path.push_back( row ); + Glib::ustring new_family = current_family; + Gtk::TreeModel::iterator iter = font_list_store->get_iter( path ); + if( iter ) { + new_family = (*iter)[FontList.family]; + } + + std::pair<Glib::ustring, Glib::ustring> ui = set_font_family( new_family, check_style ); + +#ifdef DEBUG_FONT + std::cout << "FontLister::set_font_family( row ): end" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; +#endif + return ui; + } + + + // void + // FontLister::new_font_style (Glib::ustring new_style) { + // // Is this needed? What do we do? + // } + + void + FontLister::set_font_style (Glib::ustring new_style) { + + // TODO: Validate input using Pango. If Pango doesn't recognize a style it will + // attach the "invalid" style to the font-family. + +#ifdef DEBUG_FONT + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister:set_font_style: " << new_style << std::endl; +#endif + + current_style = new_style; + current_fontspec = canonize_fontspec( current_family + ", " + current_style ); + current_fontspec_system = system_fontspec( current_fontspec ); + +#ifdef DEBUG_FONT + std::cout << " canonized: :" << current_fontspec << ":" << std::endl; + std::cout << " system: :" << current_fontspec_system << ":" << std::endl; + std::cout << " family: " << current_family << std::endl; + std::cout << " style: " << current_style << std::endl; + std::cout << "FontLister::set_font_style: end" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; +#endif + } + + // For use by font-selector where we already know that the style is valid + void + FontLister::set_font (Glib::ustring new_family, Glib::ustring new_style) { + +#ifdef DEBUG_FONT + std::cout << "FonLister::set_font: " << new_family << " " << new_style << std::endl; +#endif + set_font_family( new_family, false ); + set_font_style( new_style ); + } + + // We do this ourselves as we can't rely on FontFactory. + void + FontLister::set_css( SPCSSAttr *css ) { + + //std::cout << "FontLister:set_css: " << std::endl; + + sp_repr_css_set_property (css, "-inkscape-font-specification", current_fontspec.c_str() ); + sp_repr_css_set_property (css, "font-family", current_family.c_str() ); //Canonized w/ spaces + + PangoFontDescription *desc = pango_font_description_from_string( current_fontspec.c_str() ); + PangoWeight weight = pango_font_description_get_weight( desc ); + switch ( weight ) { + case PANGO_WEIGHT_THIN: + sp_repr_css_set_property (css, "font-weight", "100" ); + break; + case PANGO_WEIGHT_ULTRALIGHT: + sp_repr_css_set_property (css, "font-weight", "200" ); + break; + case PANGO_WEIGHT_LIGHT: + sp_repr_css_set_property (css, "font-weight", "300" ); + break; + case PANGO_WEIGHT_BOOK: + sp_repr_css_set_property (css, "font-weight", "380" ); + break; + case PANGO_WEIGHT_NORMAL: + sp_repr_css_set_property (css, "font-weight", "normal" ); + break; + case PANGO_WEIGHT_MEDIUM: + sp_repr_css_set_property (css, "font-weight", "500" ); + break; + case PANGO_WEIGHT_SEMIBOLD: + sp_repr_css_set_property (css, "font-weight", "600" ); + break; + case PANGO_WEIGHT_BOLD: + sp_repr_css_set_property (css, "font-weight", "bold" ); + break; + case PANGO_WEIGHT_ULTRABOLD: + sp_repr_css_set_property (css, "font-weight", "800" ); + break; + case PANGO_WEIGHT_HEAVY: + sp_repr_css_set_property (css, "font-weight", "900" ); + break; + case PANGO_WEIGHT_ULTRAHEAVY: + sp_repr_css_set_property (css, "font-weight", "1000" ); + break; + } + + PangoStyle style = pango_font_description_get_style( desc ); + switch ( style ) { + case PANGO_STYLE_NORMAL: + sp_repr_css_set_property (css, "font-style", "normal" ); + break; + case PANGO_STYLE_OBLIQUE: + sp_repr_css_set_property (css, "font-style", "oblique" ); + break; + case PANGO_STYLE_ITALIC: + sp_repr_css_set_property (css, "font-style", "italic" ); + break; + } + + PangoStretch stretch = pango_font_description_get_stretch( desc ); + switch ( stretch ) { + case PANGO_STRETCH_ULTRA_CONDENSED: + sp_repr_css_set_property (css, "font-stretch", "ultra-condensed" ); + break; + case PANGO_STRETCH_EXTRA_CONDENSED: + sp_repr_css_set_property (css, "font-stretch", "extra-condensed" ); + break; + case PANGO_STRETCH_CONDENSED: + sp_repr_css_set_property (css, "font-stretch", "condensed" ); + break; + case PANGO_STRETCH_SEMI_CONDENSED: + sp_repr_css_set_property (css, "font-stretch", "semi-condensed" ); + break; + case PANGO_STRETCH_NORMAL: + sp_repr_css_set_property (css, "font-stretch", "normal" ); + break; + case PANGO_STRETCH_SEMI_EXPANDED: + sp_repr_css_set_property (css, "font-stretch", "semi-expanded" ); + break; + case PANGO_STRETCH_EXPANDED: + sp_repr_css_set_property (css, "font-stretch", "expanded" ); + break; + case PANGO_STRETCH_EXTRA_EXPANDED: + sp_repr_css_set_property (css, "font-stretch", "extra-expanded" ); + break; + case PANGO_STRETCH_ULTRA_EXPANDED: + sp_repr_css_set_property (css, "font-stretch", "ultra-expanded" ); + break; + } + + PangoVariant variant = pango_font_description_get_variant( desc ); + switch ( variant ) { + case PANGO_VARIANT_NORMAL: + sp_repr_css_set_property (css, "font-variant", "normal" ); + break; + case PANGO_VARIANT_SMALL_CAPS: + sp_repr_css_set_property (css, "font-variant", "small-caps" ); + break; + } + } + + // We do this ourselves as we can't rely on FontFactory. + Glib::ustring + FontLister::fontspec_from_style (SPStyle* style) { + + //std::cout << "FontLister:fontspec_from_style: " << std::endl; + + Glib::ustring fontspec; + if (style) { + + // First try to use the font specification if it is set + if (style->text->font_specification.set + && style->text->font_specification.value + && *style->text->font_specification.value) { + + fontspec = style->text->font_specification.value; + + } else { + + fontspec = style->text->font_family.value; + fontspec += ","; + + switch (style->font_weight.computed) { + + case SP_CSS_FONT_WEIGHT_100: + fontspec += " 100"; + break; + + case SP_CSS_FONT_WEIGHT_200: + fontspec += " 200"; + break; + + case SP_CSS_FONT_WEIGHT_300: + fontspec += " 300"; + break; + + case SP_CSS_FONT_WEIGHT_400: + case SP_CSS_FONT_WEIGHT_NORMAL: + //fontspec += " normal"; + break; + + case SP_CSS_FONT_WEIGHT_500: + fontspec += " 500"; + break; + + case SP_CSS_FONT_WEIGHT_600: + fontspec += " 600"; + break; + + case SP_CSS_FONT_WEIGHT_700: + case SP_CSS_FONT_WEIGHT_BOLD: + fontspec += " bold"; + break; + + case SP_CSS_FONT_WEIGHT_800: + fontspec += " 800"; + break; + + case SP_CSS_FONT_WEIGHT_900: + fontspec += " 900"; + break; + + case SP_CSS_FONT_WEIGHT_LIGHTER: + case SP_CSS_FONT_WEIGHT_BOLDER: + default: + g_warning("Unrecognized font_weight.computed value"); + break; + } + + switch (style->font_style.computed) { + case SP_CSS_FONT_STYLE_ITALIC: + fontspec += " italic"; + break; + + case SP_CSS_FONT_STYLE_OBLIQUE: + fontspec += " oblique"; + break; + + case SP_CSS_FONT_STYLE_NORMAL: + default: + //fontspec += " normal"; + break; + } + + switch (style->font_stretch.computed) { + + case SP_CSS_FONT_STRETCH_ULTRA_CONDENSED: + fontspec += " extra_condensed"; + break; + + case SP_CSS_FONT_STRETCH_EXTRA_CONDENSED: + fontspec += " extra_condensed"; + break; + + case SP_CSS_FONT_STRETCH_CONDENSED: + case SP_CSS_FONT_STRETCH_NARROWER: + fontspec += " condensed"; + break; + + case SP_CSS_FONT_STRETCH_SEMI_CONDENSED: + fontspec += " semi_condensed"; + break; + + case SP_CSS_FONT_STRETCH_NORMAL: + //fontspec += " normal"; + break; + + case SP_CSS_FONT_STRETCH_SEMI_EXPANDED: + fontspec += " semi_expanded"; + break; + + case SP_CSS_FONT_STRETCH_EXPANDED: + case SP_CSS_FONT_STRETCH_WIDER: + fontspec += " expanded"; + break; + + case SP_CSS_FONT_STRETCH_EXTRA_EXPANDED: + fontspec += " extra_expanded"; + break; + + case SP_CSS_FONT_STRETCH_ULTRA_EXPANDED: + fontspec += " ultra_expanded"; + break; + + default: + //fontspec += " normal"; + break; + } + + switch (style->font_variant.computed) { + + case SP_CSS_FONT_VARIANT_SMALL_CAPS: + fontspec += "small-caps"; + break; + + default: + //fontspec += "normal"; + break; + } + } + } + return canonize_fontspec( fontspec ); + } + + + Gtk::TreeModel::Row FontLister::get_row_for_font (Glib::ustring family) { Gtk::TreePath path; @@ -201,8 +821,8 @@ namespace Inkscape Gtk::TreeModel::Row row = *iter; - if( family.compare( row[FontList.font] ) == 0 ) { - return font_list_store->get_path( iter ); + if( family.compare( row[FontList.family] ) == 0 ) { + return row; } ++iter; @@ -211,6 +831,65 @@ namespace Inkscape throw FAMILY_NOT_FOUND; } + Gtk::TreePath + FontLister::get_path_for_font (Glib::ustring family) + { + return font_list_store->get_path( get_row_for_font ( family ) ); + } + + /* Returns style string */ + // TODO: Remove or turn into function to be used by new_font_family. + Glib::ustring + FontLister::get_best_style_match (Glib::ustring family, Glib::ustring target_style) { + +#ifdef DEBUG_FONT + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::get_best_style_match: " << family << " : " << target_style << std::endl; +#endif + + Glib::ustring font_string = family + " " + target_style; + + Gtk::TreeModel::Row row; + try { + row = get_row_for_font( family ); + } catch (...) { + //std::cout << " ERROR: can't find family: " << family << std::endl; + return (target_style); + } + + PangoFontDescription* target = pango_font_description_from_string( font_string.c_str() ); + PangoFontDescription* best = NULL; + + //std::cout << " Target: " << pango_font_description_to_string( target ) << std::endl; + + GList* styles = row[FontList.styles]; + for (GList *l=styles; l; l = l->next) { + Glib::ustring font_string_test = family + " " + (char*)l->data; + PangoFontDescription* candidate = pango_font_description_from_string( font_string_test.c_str() ); + // std::cout << " Testing: " << pango_font_description_to_string( candidate ) << std::endl; + if( pango_font_description_better_match( target, best, candidate ) ) { + best = candidate; + } + } + + Glib::ustring best_style; + if( best ) { + //std::cout << " Best: " << pango_font_description_to_string( best ) << std::endl; + pango_font_description_unset_fields( best, PANGO_FONT_MASK_FAMILY ); + best_style = pango_font_description_to_string( best ); + } else { + //std::cout << " Failed: " << family << std::endl; + best_style = target_style; + } + +#ifdef DEBUG_FONT + std::cout << " Returning: " << best_style << std::endl; + std::cout << "FontLister::get_best_style_match: exit" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; +#endif + return best_style; + } + FontLister::~FontLister () { }; @@ -220,6 +899,18 @@ namespace Inkscape { return font_list_store; } + + const Glib::RefPtr<Gtk::ListStore> + FontLister::get_style_list () const + { + return style_list_store; + } + + const Glib::RefPtr<Gtk::ListStore> + FontLister::get_style_list_trial () const + { + return style_list_store_trial; + } } // Helper functions diff --git a/src/libnrtype/font-lister.h b/src/libnrtype/font-lister.h index 751350407..aaa996247 100644 --- a/src/libnrtype/font-lister.h +++ b/src/libnrtype/font-lister.h @@ -25,12 +25,35 @@ class SPObject; class SPDocument; +class SPCSSAttr; +struct SPStyle; namespace Inkscape { /** * This class enumerates fonts using libnrtype into reusable data stores and - * allows for random access to the font list + * allows for random access to the font-family list and the font-style list. + * Setting the font-family updates the font-style list. "Style" in this case + * refers to everything but family and size (e.g. italic/oblique, weight). + * + * This class handles font-family lists and fonts that are not on the system, + * where there is not an entry in the fontInstanceMap. + * + * This class uses the idea of "font_spec". This is a plain text string as used by + * Pango. It is similar to the CSS font shorthand except that font-family comes + * first and in this class the font-size is not used. + * + * This class uses the FontFactory class to get a list of system fonts + * and to find best matches via Pango. The Pango interface is only setup + * to deal with fonts that are on the system so care must be taken. For + * example, best matches should only be done with the first font-family + * in a font-family list. If the first font-family is not on the system + * then a generic font-family should be used (sans-serif -> Sans). + * + * This class is used by the UI interface (text-toolbar, font-select, etc.). + * + * "Font" includes family and style. It should not be used when one + * means font-family. */ class FontLister { @@ -38,13 +61,14 @@ namespace Inkscape enum Exceptions { - FAMILY_NOT_FOUND + FAMILY_NOT_FOUND, + STYLE_NOT_FOUND }; virtual ~FontLister (); - /** GtkTreeModelColumnRecord for the font list Gtk::ListStore + /** GtkTreeModelColumnRecord for the font-family list Gtk::ListStore */ class FontListClass : public Gtk::TreeModelColumnRecord @@ -52,10 +76,9 @@ namespace Inkscape public: /** Column containing the family name */ - Gtk::TreeModelColumn<Glib::ustring> font; + Gtk::TreeModelColumn<Glib::ustring> family; - /** Column containing an std::vector<std::string> with style names - * for the corresponding family + /** Column containing the styles for each family name. */ Gtk::TreeModelColumn<GList*> styles; @@ -65,7 +88,7 @@ namespace Inkscape FontListClass () { - add (font); + add (family); add (styles); add (onSystem); } @@ -73,7 +96,24 @@ namespace Inkscape FontListClass FontList; - /** Returns the ListStore with the font names + class FontStyleListClass + : public Gtk::TreeModelColumnRecord + { + public: + /** Column containing the styles + */ + Gtk::TreeModelColumn<Glib::ustring> styles; + + FontStyleListClass () + { + add (styles); + } + }; + + FontStyleListClass FontStyleList; + FontStyleListClass FontStyleListTrial; + + /** Returns the ListStore with the family names * * The return is const and the function is declared as const. * The ListStore is ready to be used after class instantiation @@ -82,6 +122,18 @@ namespace Inkscape const Glib::RefPtr<Gtk::ListStore> get_font_list () const; + /** Returns the ListStore with the styles + * + */ + const Glib::RefPtr<Gtk::ListStore> + get_style_list () const; + + /** Returns the ListStore with the styles - trial + * + */ + const Glib::RefPtr<Gtk::ListStore> + get_style_list_trial () const; + /** Updates font list to include fonts in document * */ @@ -96,13 +148,136 @@ namespace Inkscape static Inkscape::FontLister* get_instance () { - static Inkscape::FontLister* instance = new Inkscape::FontLister(); + static Inkscape::FontLister* instance = new Inkscape::FontLister(); return instance; } - Gtk::TreePath + /** Takes a hand written font spec and returns a Pango generated one in + * standard form. + */ + Glib::ustring canonize_fontspec( Glib::ustring fontspec ); + + /** Find closest system font to given font. + */ + Glib::ustring system_fontspec( Glib::ustring fontspec ); + + /** Gets font-family and style from fontspec. + * font-family and style returned. + */ + std::pair<Glib::ustring, Glib::ustring> + ui_from_fontspec (Glib::ustring fontspec); + + /** Sets font-family and style after a selection change. + * New font-family and style returned. + */ + std::pair<Glib::ustring, Glib::ustring> + selection_update (); + + /** Changes font-family, updating style list and attempting to find + * closest style to current_style style (if check_style is true). + * New font-family and style returned. + * Does NOT update current_family and current_style. + * (For potential use in font-selector which doesn't update until + * "Apply" button clicked.) + */ + std::pair<Glib::ustring, Glib::ustring> + new_font_family (Glib::ustring family, gboolean check_style = true); + + /** Sets font-family, updating style list and attempting + * to find closest style to old current_style. + * New font-family and style returned. + * Updates current_family and current_style. + * Calls new_font_family(). + * (For use in text-toolbar where update is immediate.) + */ + std::pair<Glib::ustring, Glib::ustring> + set_font_family (Glib::ustring family, gboolean check_style = true); + + /** Sets font-family from row in list store. + * The row can be used to determine if we are in the + * document or system part of the font-family list. + * This is needed to handle scrolling through the + * font-family list correctly. + * Calls set_font_family(). + */ + std::pair<Glib::ustring, Glib::ustring> + set_font_family (int row, gboolean check_style = true); + + Glib::ustring + get_font_family () + { + return current_family; + } + + int + get_font_family_row () + { + return current_family_row; + } + + /* Not Used */ + void + new_font_style (Glib::ustring style); + + /** Sets style. Does not validate style for family. + */ + void + set_font_style (Glib::ustring style); + + Glib::ustring + get_font_style () + { + return current_style; + } + + /** Sets both family and style. Does not attempt to find + * best match for style (assume that style is already valid + * for family). + */ + void + set_font (Glib::ustring family, Glib::ustring style); + + /** Sets both family and style. Does not attempt to find + * best match for style (assume that style is already valid + * for family). + */ + void + new_font (Glib::ustring family, Glib::ustring style); + + std::pair<Glib::ustring, Glib::ustring> + get_try_font () { + return ( std::make_pair( try_family, try_style ) ); + } + + Glib::ustring + fontspec_from_style (SPStyle* style); + + /** Fill css using current_fontspec. + */ + void + set_css( SPCSSAttr *css ); + + Gtk::TreeModel::Row get_row_for_font (Glib::ustring family); + Gtk::TreePath + get_path_for_font (Glib::ustring family); + + Gtk::TreeModel::Row + get_row_for_style (Glib::ustring style); + + Gtk::TreePath + get_path_for_style (Glib::ustring style); + + std::pair<Gtk::TreePath, Gtk::TreePath> + get_paths (Glib::ustring family, Glib::ustring style); + + /** Return best style match for new font given style for old font. + */ + Glib::ustring + get_best_style_match (Glib::ustring family, Glib::ustring style); + + /* Not Used */ const NRNameList get_name_list () const { @@ -116,7 +291,31 @@ namespace Inkscape NRNameList families; Glib::RefPtr<Gtk::ListStore> font_list_store; + Glib::RefPtr<Gtk::ListStore> style_list_store; + Glib::RefPtr<Gtk::ListStore> style_list_store_trial; + /** Info for currently selected font (what is shown in the UI). + * May include font-family lists and fonts not on system. + */ + int current_family_row; + Glib::ustring current_family; + Glib::ustring current_style; + Glib::ustring current_fontspec; + + /** fontspec of system font closest to current_fontspec. + * (What the system will use to display current_fontspec.) + */ + Glib::ustring current_fontspec_system; + + /** Info for proposed font (what is shown in the font-selection UI). + * May include font-family lists and fonts not on system. + */ + Glib::ustring try_family; + Glib::ustring try_style; + + /** If a font-family is not on system, this list of styles is used. + */ + GList *default_styles; }; } @@ -125,7 +324,7 @@ namespace Inkscape static gboolean font_lister_separator_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer /*data*/) { gchar* text = 0; - gtk_tree_model_get(model, iter, 0, &text, -1 ); // Column 0: FontList.font + gtk_tree_model_get(model, iter, 0, &text, -1 ); // Column 0: FontList.family return (text && strcmp(text,"#") == 0); } diff --git a/src/livarot/PathCutting.cpp b/src/livarot/PathCutting.cpp index 9e6226ccc..848d8daa8 100644 --- a/src/livarot/PathCutting.cpp +++ b/src/livarot/PathCutting.cpp @@ -1126,6 +1126,8 @@ void Path::ConvertPositionsToForced(int nbPos, cut_position *poss) } } } + if (descr_cmd[0]->getType() == descr_moveto) + descr_flags |= descr_doing_subpath; // see LP Bug 166302 qsort(poss, nbPos, sizeof(cut_position), CmpPosition); diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp index d046ad683..ed4791155 100644 --- a/src/live_effects/lpe-bspline.cpp +++ b/src/live_effects/lpe-bspline.cpp @@ -52,12 +52,6 @@ LPEBSpline::doEffect(SPCurve * curve) //los puntos clave para los manejadores. //Esto hace que la curva BSpline no pierda su condición aunque se trasladen //dichos manejadores - SPCurve * in = new SPCurve(); - in->moveto(curve_it1->initialPoint()); - in->lineto(curve_it1->finalPoint()); - SPCurve * out = new SPCurve(); - out->moveto(curve_it2->initialPoint()); - out->lineto(curve_it2->finalPoint()); SPCurve *nCurve = new SPCurve(); Geom::Point startNode(0,0); Geom::Point previousNode(0,0); @@ -67,6 +61,9 @@ LPEBSpline::doEffect(SPCurve * curve) Geom::Point nextPointAt1(0,0); Geom::Point nextPointAt2(0,0); Geom::Point nextPointAt3(0,0); + Geom::D2< Geom::SBasis > SBasisIn; + Geom::D2< Geom::SBasis > SBasisOut; + Geom::D2< Geom::SBasis > SBasisHelper; Geom::CubicBezier const *cubic = NULL; //Si la curva está cerrada calculamos el punto donde //deveria estar el nodo BSpline de cierre/inicio de la curva @@ -78,13 +75,12 @@ LPEBSpline::doEffect(SPCurve * curve) if (are_near(closingline.initialPoint(), closingline.finalPoint())) { curve_endit = path_it->end_open(); } - SPCurve * end = new SPCurve(); - end->moveto(curve_endit->initialPoint()); - end->lineto(curve_endit->finalPoint()); - Geom::D2< Geom::SBasis > SBasisIn = in->first_segment()->toSBasis(); - Geom::D2< Geom::SBasis > SBasisEnd = end->first_segment()->toSBasis(); - end->reset(); - delete end; + + SPCurve * in = new SPCurve(); + in->moveto(curve_it1->initialPoint()); + in->lineto(curve_it1->finalPoint()); + SBasisIn = in->first_segment()->toSBasis(); + SPCurve *lineHelper = new SPCurve(); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it1); if(cubic){ @@ -92,13 +88,23 @@ LPEBSpline::doEffect(SPCurve * curve) }else{ lineHelper->moveto(in->first_segment()->initialPoint()); } + in->reset(); + delete in; + + SPCurve * end = new SPCurve(); + end->moveto(curve_endit->initialPoint()); + end->lineto(curve_endit->finalPoint()); + Geom::D2< Geom::SBasis > SBasisEnd = end->first_segment()->toSBasis(); + //Geom::BezierCurve const *bezier = dynamic_cast<Geom::BezierCurve const*>(&*curve_endit); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_endit); if(cubic){ lineHelper->lineto(SBasisEnd.valueAt(Geom::nearest_point((*cubic)[2],*end->first_segment()))); }else{ lineHelper->lineto(end->first_segment()->finalPoint()); } - Geom::D2< Geom::SBasis > SBasisHelper = lineHelper->first_segment()->toSBasis(); + end->reset(); + delete end; + SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; //Guardamos el principio de la curva @@ -107,7 +113,12 @@ LPEBSpline::doEffect(SPCurve * curve) node = startNode; }else{ //Guardamos el principio de la curva + SPCurve * in = new SPCurve(); + in->moveto(curve_it1->initialPoint()); + in->lineto(curve_it1->finalPoint()); startNode = in->first_segment()->initialPoint(); + in->reset(); + delete in; //Definimos el punto de inicio original de la curva resultante node = startNode; } @@ -116,20 +127,28 @@ LPEBSpline::doEffect(SPCurve * curve) { //previousPointAt3 = pointAt3; //Calculamos los puntos que dividirÃan en tres segmentos iguales el path recto de entrada y de salida + SPCurve * in = new SPCurve(); + in->moveto(curve_it1->initialPoint()); + in->lineto(curve_it1->finalPoint()); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it1); if(cubic){ - Geom::D2< Geom::SBasis > SBasisIn = in->first_segment()->toSBasis(); + SBasisIn = in->first_segment()->toSBasis(); pointAt1 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[1],*in->first_segment())); pointAt2 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[2],*in->first_segment())); }else{ pointAt1 = in->first_segment()->initialPoint(); pointAt2 = in->first_segment()->finalPoint(); } + in->reset(); + delete in; //Y hacemos lo propio con el path de salida //nextPointAt0 = curveOut.valueAt(0); + SPCurve * out = new SPCurve(); + out->moveto(curve_it2->initialPoint()); + out->lineto(curve_it2->finalPoint()); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it2); if(cubic){ - Geom::D2< Geom::SBasis > SBasisOut = out->first_segment()->toSBasis(); + SBasisOut = out->first_segment()->toSBasis(); nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1],*out->first_segment())); nextPointAt2 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[2],*out->first_segment()));; nextPointAt3 = (*cubic)[3]; @@ -138,6 +157,8 @@ LPEBSpline::doEffect(SPCurve * curve) nextPointAt2 = out->first_segment()->finalPoint(); nextPointAt3 = out->first_segment()->finalPoint(); } + out->reset(); + delete out; //La curva BSpline se forma calculando el centro del segmanto de unión //de el punto situado en las 2/3 partes de el segmento de entrada //con el punto situado en la posición 1/3 del segmento de salida @@ -146,7 +167,7 @@ LPEBSpline::doEffect(SPCurve * curve) SPCurve *lineHelper = new SPCurve(); lineHelper->moveto(pointAt2); lineHelper->lineto(nextPointAt1); - Geom::D2< Geom::SBasis > SBasisHelper = lineHelper->first_segment()->toSBasis(); + SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; //almacenamos el punto del anterior bucle -o el de cierre- que nos hara de principio de curva @@ -163,18 +184,6 @@ LPEBSpline::doEffect(SPCurve * curve) //aumentamos los valores para el siguiente paso en el bucle ++curve_it1; ++curve_it2; - in->reset(); - delete in; - SPCurve * in = new SPCurve(); - in->moveto(curve_it1->initialPoint()); - in->lineto(curve_it1->finalPoint()); - out->reset(); - delete out; - if(curve_it1 != curve_endit){ - SPCurve * out = new SPCurve(); - out->moveto(curve_it2->initialPoint()); - out->lineto(curve_it2->finalPoint()); - } } //Aberiguamos la ultima parte de la curva correspondiente al último segmento SPCurve *curveHelper = new SPCurve(); @@ -197,11 +206,6 @@ LPEBSpline::doEffect(SPCurve * curve) curve->append(nCurve,false); nCurve->reset(); delete nCurve; - //Limpiamos - in->reset(); - out->reset(); - delete in; - delete out; } } diff --git a/src/pen-context.cpp b/src/pen-context.cpp index a505f8de7..0af9570c1 100644 --- a/src/pen-context.cpp +++ b/src/pen-context.cpp @@ -96,10 +96,15 @@ static void sp_pen_context_set_mode(SPPenContext *const pc, guint mode); //Esta función cambia los colores rojo,verde y azul haciendolos transparentes o no en función de si se usa spiro static void spiro_color(SPPenContext *const pc); //Guarda el valor si se ha pulsado la tecla SHIFT al continuar una curva -static bool saShift = false; - -//Preparamos la curva roja para que se muestre según esté pulsada la tecla SHIFT -static void spiro(SPPenContext *const pc, bool Shift); +static void spiro(SPPenContext *const pc,bool shift); +static void spiroOn(SPPenContext *const pc); +static void spiroOff(SPPenContext *const pc); +static void spiroStartAnchor(SPPenContext *const pc,bool shift); +static void spiroStartAnchorOn(SPPenContext *const pc); +static void spiroStartAnchorOff(SPPenContext *const pc); +static void spiroMotion(SPPenContext *const pc,bool shift); +static void spiroEndAnchorOn(SPPenContext *const pc); +static void spiroEndAnchorOff(SPPenContext *const pc); //Unimos todas las curvas en juego y llamamos a la función doEffect. static void spiro_build(SPPenContext *const pc); //function spiro cloned from lpe-spiro.cpp @@ -541,7 +546,7 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const //BSpline if(anchor){ if(pc->spiro){ - spiro(pc,(bevent.state & GDK_SHIFT_MASK)); + spiroStartAnchor(pc,(bevent.state & GDK_SHIFT_MASK)); } if(pc->bspline){ bsplineStartAnchor(pc,(bevent.state & GDK_SHIFT_MASK)); @@ -794,7 +799,7 @@ static gint pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion cons //BSpline if ( Geom::LInfty( event_w - pen_drag_origin_w ) > tolerance || mevent.time == 0) { if(pc->spiro){ - spiro(pc,(mevent.state & GDK_SHIFT_MASK)); + spiroMotion(pc,(mevent.state & GDK_SHIFT_MASK)); } if(pc->bspline){ bsplineMotion(pc,(mevent.state & GDK_SHIFT_MASK)); @@ -848,7 +853,7 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con //BSpline if (anchor) { if(pc->spiro){ - spiro(pc,(revent.state & GDK_SHIFT_MASK)); + spiroStartAnchor(pc,(revent.state & GDK_SHIFT_MASK)); } if(pc->bspline){ bsplineStartAnchor(pc,(revent.state & GDK_SHIFT_MASK)); @@ -1546,84 +1551,259 @@ static void spiro_color(SPPenContext *const pc) } -//Preparamos la curva roja para que se muestre según esté pulsada la tecla SHIFT -static void spiro(SPPenContext *const pc, bool Shift) +//Unimos todas las curvas en juego y llamamos a la función doEffect. + +static void spiro(SPPenContext *const pc, bool shift) { - if(Shift){ - if(!pc->red_curve->is_empty()){ - //Creamos un nodo CUSP - pc->npoints = 5; - pc->p[2] = pc->p[3]; - } - //Continuamos la curva en modo CUSP - //Guardamos el valor de inicio en cusp para que no se redibuje como SYMM - if(pc->anchor_statusbar && pc->red_curve->is_empty()){ - saShift = true; - } - }else{ - //Symm nodo + if(!pc->anchor_statusbar) + shift?spiroOff(pc):spiroOn(pc); + + spiro_build(pc); +} + +static void spiroOn(SPPenContext *const pc){ + if(!pc->red_curve->is_empty()){ using Geom::X; using Geom::Y; - Geom::CubicBezier const * cubic; - if(pc->anchor_statusbar && pc->red_curve->is_empty()){ - saShift = false; - } - if(!pc->red_curve->is_empty()){ - //Damos valores a los puntos de la curva roja para que generen un nodo SYMM - pc->npoints = 5; - //Les damos valor - pc->p[3] = pc->red_curve->first_segment()->finalPoint(); - pc->p[0] = pc->red_curve->first_segment()->initialPoint(); - pc->p[2] = pc->p[3] + (1./3)*(pc->p[0] - pc->p[3]); - pc->p[1] = pc->p[0] + (1./3)*(pc->p[3] - pc->p[0]); - pc->p[1] = Geom::Point(pc->p[1][X]+1,pc->p[1][Y]+1); - pc->p[2] = Geom::Point(pc->p[2][X]+1,pc->p[2][Y]+1); - pc->p[4] = pc->p[3] + (Geom::Point)( pc->p[3] - pc->p[2] ); + pc->npoints = 5; + pc->p[0] = pc->red_curve->first_segment()->initialPoint(); + pc->p[3] = pc->red_curve->first_segment()->finalPoint(); + pc->p[2] = pc->p[3] + (1./3)*(pc->p[0] - pc->p[3]); + pc->p[2] = Geom::Point(pc->p[2][X] + 0.0625,pc->p[2][Y] + 0.0625); + } +} + +static void spiroOff(SPPenContext *const pc) +{ + if(!pc->red_curve->is_empty()){ + pc->npoints = 5; + pc->p[0] = pc->red_curve->first_segment()->initialPoint(); + pc->p[3] = pc->red_curve->first_segment()->finalPoint(); + pc->p[2] = pc->p[3]; + } +} + +static void spiroStartAnchor(SPPenContext *const pc, bool shift) +{ + if(pc->sa->curve->is_empty()) + return; + + if(shift) + bsplineStartAnchorOff(pc); + else + bsplineStartAnchorOn(pc); +} + +static void spiroStartAnchorOn(SPPenContext *const pc) +{ + using Geom::X; + using Geom::Y; + SPCurve *tmpCurve = new SPCurve(); + tmpCurve = pc->sa->curve->copy(); + if(pc->sa->start) + tmpCurve = tmpCurve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + SPCurve *lastSeg = new SPCurve(); + Geom::Point A = tmpCurve->last_segment()->initialPoint(); + Geom::Point D = tmpCurve->last_segment()->finalPoint(); + Geom::Point C = D + (1./3)*(A - D); + C = Geom::Point(C[X] + 0.0625,C[Y] + 0.0625); + if(cubic){ + lastSeg->moveto(A); + lastSeg->curveto((*cubic)[1],C,D); + }else{ + lastSeg->moveto(A); + lastSeg->curveto(A,C,D); + } + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + if (pc->sa->start) { + tmpCurve = tmpCurve->create_reverse(); + } + pc->sa->curve->reset(); + pc->sa->curve = tmpCurve; +} + +static void spiroStartAnchorOff(SPPenContext *const pc) +{ + SPCurve *tmpCurve = new SPCurve(); + tmpCurve = pc->sa->curve->copy(); + if(pc->sa->start) + tmpCurve = tmpCurve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(cubic){ + SPCurve *lastSeg = new SPCurve(); + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + if (pc->sa->start) { + tmpCurve = tmpCurve->create_reverse(); } + pc->sa->curve->reset(); + pc->sa->curve = tmpCurve; + } - //Continuamos la curva en modo SPIRO - if( saShift == false && pc->sa && !pc->sa->curve->is_empty() && !pc->ea && pc->green_curve->is_empty()){ - cubic = dynamic_cast<Geom::CubicBezier const *>( pc->sa->curve->last_segment()); - if (pc->sa->start) { - cubic = dynamic_cast<Geom::CubicBezier const *>( pc->sa->curve->create_reverse()->last_segment() ); - } - if(cubic){ - pc->p[1] = (pc->p[0] - (*cubic)[2]) + pc->p[0]; - } +} + +static void spiroMotion(SPPenContext *const pc, bool shift){ + using Geom::X; + using Geom::Y; + SPCurve *tmpCurve = new SPCurve(); + if(shift) + pc->p[2] = pc->p[3]; + else + pc->p[2] = pc->p[3] + (1./3)*(pc->p[0] - pc->p[3]); + pc->p[2] = Geom::Point(pc->p[2][X] + 0.0625,pc->p[2][Y] + 0.0625); + + if(pc->green_curve->is_empty() && !pc->sa){ + pc->p[1] = pc->p[0] + (1./3)*(pc->p[3] - pc->p[0]); + }else if(!pc->green_curve->is_empty()){ + tmpCurve = pc->green_curve->copy(); + }else{ + tmpCurve = pc->sa->curve->copy(); + if(pc->sa->start) + tmpCurve = tmpCurve->create_reverse(); + } + if(!tmpCurve->is_empty() && !pc->red_curve->is_empty()){ + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(cubic){ + pc->p[1] = (*cubic)[3] + (Geom::Point)((*cubic)[3] - (*cubic)[2] ); + }else{ + pc->p[1] = pc->p[0]; } + } - //Nos aseguramos que el primer nodo sea SYMM con el ultimo segmento de la curva verde - if(!pc->green_curve->is_empty()){ - cubic = dynamic_cast<Geom::CubicBezier const *>( pc->green_curve->last_segment()); - if(cubic && (*cubic)[2] != (*cubic)[3]){ - pc->p[1] = (pc->p[0] - (*cubic)[2]) + pc->p[0]; - } - //Damos valor original a la variable por si se necesita de nuevo - saShift = false; + if(pc->anchor_statusbar && !pc->red_curve->is_empty()){ + if(shift) + bsplineEndAnchorOff(pc); + else + bsplineEndAnchorOn(pc); + } + + spiro_build(pc); +} + +static void spiroEndAnchorOn(SPPenContext *const pc) +{ + using Geom::X; + using Geom::Y; + pc->p[2] = pc->p[3] + (1./3)*(pc->p[0] - pc->p[3]); + pc->p[2] = Geom::Point(pc->p[2][X] + 0.0625,pc->p[2][Y] + 0.0625); + SPCurve *tmpCurve = new SPCurve(); + SPCurve *lastSeg = new SPCurve(); + Geom::Point C(0,0); + if(!pc->sa || pc->sa->curve->is_empty()){ + tmpCurve = pc->green_curve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + C = tmpCurve->last_segment()->finalPoint() + + (Geom::Point)(tmpCurve->last_segment()->finalPoint() - pc->p[2]); + if(cubic){ + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],C,(*cubic)[3]); + }else{ + lastSeg->moveto(tmpCurve->last_segment()->initialPoint()); + lastSeg->curveto(tmpCurve->last_segment()->initialPoint(),C,tmpCurve->last_segment()->finalPoint()); } + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + tmpCurve = tmpCurve->create_reverse(); + pc->green_curve->reset(); + pc->green_curve = tmpCurve; + }else{ + tmpCurve = pc->sa->curve->copy(); + if(!pc->sa->start) + tmpCurve = tmpCurve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + C = tmpCurve->last_segment()->finalPoint() + + (Geom::Point)(tmpCurve->last_segment()->finalPoint() - pc->p[2]); + if(cubic){ + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],C,(*cubic)[3]); + }else{ + lastSeg->moveto(tmpCurve->last_segment()->initialPoint()); + lastSeg->curveto(tmpCurve->last_segment()->initialPoint(),C,tmpCurve->last_segment()->finalPoint()); + } + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + if (!pc->sa->start) { + tmpCurve = tmpCurve->create_reverse(); + } + pc->sa->curve->reset(); + pc->sa->curve = tmpCurve; + } +} - //Si cerramos sobre otra curva ponemos simetrico a esta el manejador del punto de cierre - if(pc->anchor_statusbar && pc->sa && !Geom::are_near(pc->sa->dp, pc->red_curve->first_segment()->finalPoint())){ - cubic = dynamic_cast<Geom::CubicBezier const *>( pc->sa->curve->last_segment() ); - if (!pc->sa->start) { - cubic = dynamic_cast<Geom::CubicBezier const *>( pc->sa->curve->create_reverse()->last_segment() ); - } - if(cubic){ - pc->p[2] = (pc->p[3]-(*cubic)[2]) + pc->p[3]; +static void spiroEndAnchorOff(SPPenContext *const pc) +{ + pc->p[2] = pc->p[3]; + SPCurve *tmpCurve = new SPCurve(); + SPCurve *lastSeg = new SPCurve(); + if(!pc->sa || pc->sa->curve->is_empty()){ + tmpCurve = pc->green_curve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(cubic){ + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); } + tmpCurve = tmpCurve->create_reverse(); + pc->green_curve->reset(); + pc->green_curve = tmpCurve; } - //Lo mismpo pero cerrando sobre la curva inicial - if(pc->anchor_statusbar && pc->green_anchor && pc->green_anchor->active){ - cubic = dynamic_cast<Geom::CubicBezier const *>( pc->green_curve->first_segment() ); - if(cubic){ - pc->p[2] = (pc->p[3]-(*cubic)[1]) + pc->p[3]; + }else{ + tmpCurve = pc->sa->curve->copy(); + if(!pc->sa->start) + tmpCurve = tmpCurve->create_reverse(); + Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); + if(cubic){ + lastSeg->moveto((*cubic)[0]); + lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); + if( tmpCurve->get_segment_count() == 1){ + tmpCurve = lastSeg; + }else{ + //we eliminate the last segment + tmpCurve->backspace(); + //and we add it again with the recreation + tmpCurve->append_continuous(lastSeg, 0.0625); + } + if (!pc->sa->start) { + tmpCurve = tmpCurve->create_reverse(); } + pc->sa->curve->reset(); + pc->sa->curve = tmpCurve; } } - spiro_build(pc); } - //Unimos todas las curvas en juego y llamamos a la función doEffect. static void spiro_build(SPPenContext *const pc) { @@ -2054,143 +2234,6 @@ static void bsplineEndAnchorOff(SPPenContext *const pc) } } -/* -//Unimos todas las curvas en juego y llamamos a la función doEffect. -static void bspline(SPPenContext *const pc, bool Shift) -{ - using Geom::X; - using Geom::Y; - Geom::CubicBezier const *cubic; - if(Shift){ - - //Continuamos la curva en modo CUSP - //Guardamos el valor de inicio en cusp para que no se redibuje como SYMM - if(pc->anchor_statusbar && pc->red_curve->is_empty()){ - SPCurve *previousCurve = new SPCurve(); - if(pc->sa && !pc->sa->curve->is_empty()){ - previousCurve = pc->sa->curve->copy(); - if (pc->sa->start) { - previousCurve = previousCurve->create_reverse(); - } - cubic = dynamic_cast<Geom::CubicBezier const*>(&*previousCurve->last_segment()); - if(cubic){ - SPCurve *lastSeg = new SPCurve(); - lastSeg->moveto((*cubic)[0]); - lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); - previousCurve->backspace(); - previousCurve->append_continuous(lastSeg, 0.0625); - if (pc->sa->start) { - previousCurve = previousCurve->create_reverse(); - } - pc->sa->curve->reset(); - pc->sa->curve = previousCurve; - } - } - } - - if(pc->anchor_statusbar && !pc->red_curve->is_empty()){ - // Step B - both start and end anchored to same curve - if ( pc->sa && pc->ea - && ( pc->sa->curve == pc->ea->curve ) - && ( ( pc->sa != pc->ea ) - || pc->sa->curve->is_closed() ) ) - { - SPCurve * same = pc->sa->curve->copy(); - if (pc->sa->start) { - same = same->create_reverse(); - } - cubic = dynamic_cast<Geom::CubicBezier const*>(&*same->last_segment()); - if(cubic){ - SPCurve *lastSeg = new SPCurve(); - lastSeg->moveto((*cubic)[0]); - lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); - same->backspace(); - same->append_continuous(lastSeg, 0.0625); - if (pc->sa->start) { - same = same->create_reverse(); - } - pc->sa->curve->reset(); - pc->sa->curve = same; - } - } - // Step A - test, whether we ended on green anchor - if ( pc->green_anchor && pc->green_anchor->active ) { - // We hit green anchor, closing Green-Blue-Red - SPCurve * green = pc->green_curve->copy()->create_reverse(); - cubic = dynamic_cast<Geom::CubicBezier const*>(&*green->last_segment()); - if(cubic){ - SPCurve *lastSeg = new SPCurve(); - lastSeg->moveto((*cubic)[0]); - lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]); - green->backspace(); - green->append_continuous(lastSeg, 0.0625); - green = green->create_reverse(); - pc->green_curve->reset(); - pc->green_curve = green; - } - } - } - - if (!pc->red_curve->is_empty()){ - pc->npoints = 5; - pc->p[0] = pc->red_curve->first_segment()->initialPoint(); - pc->p[3] = pc->red_curve->first_segment()->finalPoint(); - if(pc->green_curve->is_empty()){ - pc->p[1] = pc->p[0]; - }else{ - cubic = dynamic_cast<Geom::CubicBezier const*>(&*pc->green_curve->last_segment()); - if(!cubic || ((*cubic)[2] == (*cubic)[3])){ - pc->p[1] = pc->p[0]; - pc->p[2] = pc->p[3]; - }else{ - pc->p[1] = pc->p[0] + (1./3)*(pc->p[3] - pc->p[0]); - pc->p[1] = Geom::Point(pc->p[1][X]+1,pc->p[1][Y]+1); - pc->p[2] = pc->p[3]; - } - - } - } - }else{ - //NODO CUSP formado por nodo SMOOTH - //solo mobemos el manejador del nodo final de cada segmento - //Es suficiente para mostrar el nodo como CUSP - //Usamos 5 puntos - if(!pc->red_curve->is_empty()){ - pc->npoints = 5; - pc->p[0] = pc->red_curve->first_segment()->initialPoint(); - pc->p[3] = pc->red_curve->first_segment()->finalPoint(); - if(pc->green_curve->is_empty()){ - if(!pc->sa || (pc->sa && pc->p[0] != pc->p[1])){ - pc->p[1] = pc->p[0] + (1./3)*(pc->p[3] - pc->p[0]); - pc->p[1] = Geom::Point(pc->p[1][X]+1,pc->p[1][Y]+1); - } - }else{ - cubic = dynamic_cast<Geom::CubicBezier const*>(&*pc->green_curve->last_segment()); - if(!cubic || ((*cubic)[2] == (*cubic)[3])) - pc->p[1] = pc->p[0]; - else{ - SPCurve * WPower = new SPCurve(); - Geom::D2< Geom::SBasis > SBasisWPower; - WPower->moveto(pc->green_curve->last_segment()->finalPoint()); - WPower->lineto(pc->green_curve->last_segment()->initialPoint()); - float WP = Geom::nearest_point((*cubic)[2],*WPower->first_segment()); - WPower->reset(); - WPower->moveto(pc->red_curve->last_segment()->initialPoint()); - WPower->lineto(pc->red_curve->last_segment()->finalPoint()); - SBasisWPower = WPower->first_segment()->toSBasis(); - WPower->reset(); - pc->p[1] = SBasisWPower.valueAt(WP); - pc->p[1] = Geom::Point(pc->p[1][X]+1,pc->p[1][Y]+1); - } - } - pc->p[2] = pc->p[3] + (1./3)*(pc->p[0] - pc->p[3]); - pc->p[2] = Geom::Point(pc->p[2][X]+1,pc->p[2][Y]+1); - } - } - bspline_build(pc); -} - -/*/ //preparates the curves for its trasformation into BSline curves. static void bspline_build(SPPenContext *const pc) @@ -2267,12 +2310,6 @@ static void bspline_doEffect(SPCurve * curve) //los puntos clave para los manejadores. //Esto hace que la curva BSpline no pierda su condición aunque se trasladen //dichos manejadores - SPCurve * in = new SPCurve(); - in->moveto(curve_it1->initialPoint()); - in->lineto(curve_it1->finalPoint()); - SPCurve * out = new SPCurve(); - out->moveto(curve_it2->initialPoint()); - out->lineto(curve_it2->finalPoint()); SPCurve *nCurve = new SPCurve(); Geom::Point startNode(0,0); Geom::Point previousNode(0,0); @@ -2282,6 +2319,9 @@ static void bspline_doEffect(SPCurve * curve) Geom::Point nextPointAt1(0,0); Geom::Point nextPointAt2(0,0); Geom::Point nextPointAt3(0,0); + Geom::D2< Geom::SBasis > SBasisIn; + Geom::D2< Geom::SBasis > SBasisOut; + Geom::D2< Geom::SBasis > SBasisHelper; Geom::CubicBezier const *cubic = NULL; //Si la curva está cerrada calculamos el punto donde //deveria estar el nodo BSpline de cierre/inicio de la curva @@ -2293,13 +2333,12 @@ static void bspline_doEffect(SPCurve * curve) if (are_near(closingline.initialPoint(), closingline.finalPoint())) { curve_endit = path_it->end_open(); } - SPCurve * end = new SPCurve(); - end->moveto(curve_endit->initialPoint()); - end->lineto(curve_endit->finalPoint()); - Geom::D2< Geom::SBasis > SBasisIn = in->first_segment()->toSBasis(); - Geom::D2< Geom::SBasis > SBasisEnd = end->first_segment()->toSBasis(); - end->reset(); - delete end; + + SPCurve * in = new SPCurve(); + in->moveto(curve_it1->initialPoint()); + in->lineto(curve_it1->finalPoint()); + SBasisIn = in->first_segment()->toSBasis(); + SPCurve *lineHelper = new SPCurve(); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it1); if(cubic){ @@ -2307,13 +2346,23 @@ static void bspline_doEffect(SPCurve * curve) }else{ lineHelper->moveto(in->first_segment()->initialPoint()); } + in->reset(); + delete in; + + SPCurve * end = new SPCurve(); + end->moveto(curve_endit->initialPoint()); + end->lineto(curve_endit->finalPoint()); + Geom::D2< Geom::SBasis > SBasisEnd = end->first_segment()->toSBasis(); + //Geom::BezierCurve const *bezier = dynamic_cast<Geom::BezierCurve const*>(&*curve_endit); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_endit); if(cubic){ lineHelper->lineto(SBasisEnd.valueAt(Geom::nearest_point((*cubic)[2],*end->first_segment()))); }else{ lineHelper->lineto(end->first_segment()->finalPoint()); } - Geom::D2< Geom::SBasis > SBasisHelper = lineHelper->first_segment()->toSBasis(); + end->reset(); + delete end; + SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; //Guardamos el principio de la curva @@ -2322,7 +2371,12 @@ static void bspline_doEffect(SPCurve * curve) node = startNode; }else{ //Guardamos el principio de la curva + SPCurve * in = new SPCurve(); + in->moveto(curve_it1->initialPoint()); + in->lineto(curve_it1->finalPoint()); startNode = in->first_segment()->initialPoint(); + in->reset(); + delete in; //Definimos el punto de inicio original de la curva resultante node = startNode; } @@ -2331,20 +2385,28 @@ static void bspline_doEffect(SPCurve * curve) { //previousPointAt3 = pointAt3; //Calculamos los puntos que dividirÃan en tres segmentos iguales el path recto de entrada y de salida + SPCurve * in = new SPCurve(); + in->moveto(curve_it1->initialPoint()); + in->lineto(curve_it1->finalPoint()); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it1); if(cubic){ - Geom::D2< Geom::SBasis > SBasisIn = in->first_segment()->toSBasis(); + SBasisIn = in->first_segment()->toSBasis(); pointAt1 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[1],*in->first_segment())); pointAt2 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[2],*in->first_segment())); }else{ pointAt1 = in->first_segment()->initialPoint(); pointAt2 = in->first_segment()->finalPoint(); } + in->reset(); + delete in; //Y hacemos lo propio con el path de salida //nextPointAt0 = curveOut.valueAt(0); + SPCurve * out = new SPCurve(); + out->moveto(curve_it2->initialPoint()); + out->lineto(curve_it2->finalPoint()); cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it2); if(cubic){ - Geom::D2< Geom::SBasis > SBasisOut = out->first_segment()->toSBasis(); + SBasisOut = out->first_segment()->toSBasis(); nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1],*out->first_segment())); nextPointAt2 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[2],*out->first_segment()));; nextPointAt3 = (*cubic)[3]; @@ -2353,6 +2415,8 @@ static void bspline_doEffect(SPCurve * curve) nextPointAt2 = out->first_segment()->finalPoint(); nextPointAt3 = out->first_segment()->finalPoint(); } + out->reset(); + delete out; //La curva BSpline se forma calculando el centro del segmanto de unión //de el punto situado en las 2/3 partes de el segmento de entrada //con el punto situado en la posición 1/3 del segmento de salida @@ -2361,7 +2425,7 @@ static void bspline_doEffect(SPCurve * curve) SPCurve *lineHelper = new SPCurve(); lineHelper->moveto(pointAt2); lineHelper->lineto(nextPointAt1); - Geom::D2< Geom::SBasis > SBasisHelper = lineHelper->first_segment()->toSBasis(); + SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; //almacenamos el punto del anterior bucle -o el de cierre- que nos hara de principio de curva @@ -2378,18 +2442,6 @@ static void bspline_doEffect(SPCurve * curve) //aumentamos los valores para el siguiente paso en el bucle ++curve_it1; ++curve_it2; - in->reset(); - delete in; - SPCurve * in = new SPCurve(); - in->moveto(curve_it1->initialPoint()); - in->lineto(curve_it1->finalPoint()); - out->reset(); - delete out; - if(curve_it1 != curve_endit){ - SPCurve * out = new SPCurve(); - out->moveto(curve_it2->initialPoint()); - out->lineto(curve_it2->finalPoint()); - } } //Aberiguamos la ultima parte de la curva correspondiente al último segmento SPCurve *curveHelper = new SPCurve(); @@ -2412,11 +2464,6 @@ static void bspline_doEffect(SPCurve * curve) curve->append(nCurve,false); nCurve->reset(); delete nCurve; - //Limpiamos - in->reset(); - out->reset(); - delete in; - delete out; } } //BSpline end diff --git a/src/shortcuts.cpp b/src/shortcuts.cpp index 5af75a9a5..755269edb 100644 --- a/src/shortcuts.cpp +++ b/src/shortcuts.cpp @@ -52,10 +52,12 @@ using Inkscape::IO::Resource::SYSTEM; using Inkscape::IO::Resource::USER; using Inkscape::IO::Resource::KEYS; - static void try_shortcuts_file(char const *filename); static void read_shortcuts_file(char const *filename, bool const is_user_set=false); +unsigned int sp_shortcut_get_key(unsigned int const shortcut); +GdkModifierType sp_shortcut_get_modifiers(unsigned int const shortcut); + /* Returns true if action was performed */ bool @@ -430,7 +432,7 @@ void sp_shortcut_delete_from_file(char const * /*action*/, unsigned int const sh return; } - gchar *key = gdk_keyval_name (shortcut & (~SP_SHORTCUT_MODIFIER_MASK)); + gchar *key = gdk_keyval_name (sp_shortcut_get_key(shortcut)); std::string modifiers = sp_shortcut_to_label(shortcut & (SP_SHORTCUT_MODIFIER_MASK)); if (!key) { @@ -502,7 +504,7 @@ void sp_shortcut_add_to_file(char const *action, unsigned int const shortcut) { } } - gchar *key = gdk_keyval_name (shortcut & (~SP_SHORTCUT_MODIFIER_MASK)); + gchar *key = gdk_keyval_name (sp_shortcut_get_key(shortcut)); std::string modifiers = sp_shortcut_to_label(shortcut & (SP_SHORTCUT_MODIFIER_MASK)); if (!key) { @@ -639,6 +641,58 @@ sp_shortcut_unset(unsigned int const shortcut) } } + +GtkAccelGroup * +sp_shortcut_get_accel_group() +{ + static GtkAccelGroup *accel_group = NULL; + + if (!accel_group) { + accel_group = gtk_accel_group_new (); + } + + return accel_group; +} + +/** + * Adds a gtk accelerator to a widget + * Used to display the keyboard shortcuts in the main menu items + */ +void +sp_shortcut_add_accelerator(GtkWidget *item, unsigned int const shortcut) +{ + if (shortcut == GDK_KEY_VoidSymbol) { + return; + } + + unsigned int accel_key = sp_shortcut_get_key(shortcut); + if (accel_key > 0) { + gtk_widget_add_accelerator (item, + "activate", + sp_shortcut_get_accel_group(), + accel_key, + sp_shortcut_get_modifiers(shortcut), + GTK_ACCEL_VISIBLE); + } +} + + +unsigned int +sp_shortcut_get_key(unsigned int const shortcut) +{ + return (shortcut & (~SP_SHORTCUT_MODIFIER_MASK)); +} + +GdkModifierType +sp_shortcut_get_modifiers(unsigned int const shortcut) +{ + return static_cast<GdkModifierType>( + ((shortcut & SP_SHORTCUT_SHIFT_MASK) ? GDK_SHIFT_MASK : 0) | + ((shortcut & SP_SHORTCUT_CONTROL_MASK) ? GDK_CONTROL_MASK : 0) | + ((shortcut & SP_SHORTCUT_ALT_MASK) ? GDK_MOD1_MASK : 0) + ); +} + /** * Adds a keyboard shortcut for the given verb. * (Removes any existing binding for the given shortcut, including appropriately @@ -706,7 +760,6 @@ bool sp_shortcut_is_user_set(Inkscape::Verb *verb) return result; } - gchar *sp_shortcut_get_label(unsigned int shortcut) { // The comment below was copied from the function sp_ui_shortcut_string in interface.cpp (which was subsequently removed) @@ -721,11 +774,8 @@ gchar *sp_shortcut_get_label(unsigned int shortcut) gchar *result = 0; if (shortcut != GDK_KEY_VoidSymbol) { result = gtk_accelerator_get_label( - shortcut & (~SP_SHORTCUT_MODIFIER_MASK), static_cast<GdkModifierType>( - ((shortcut & SP_SHORTCUT_SHIFT_MASK) ? GDK_SHIFT_MASK : 0) | - ((shortcut & SP_SHORTCUT_CONTROL_MASK) ? GDK_CONTROL_MASK : 0) | - ((shortcut & SP_SHORTCUT_ALT_MASK) ? GDK_MOD1_MASK : 0) - )); + sp_shortcut_get_key(shortcut), + sp_shortcut_get_modifiers(shortcut)); } return result; } diff --git a/src/shortcuts.h b/src/shortcuts.h index 118909bd3..c2a6f6cde 100644 --- a/src/shortcuts.h +++ b/src/shortcuts.h @@ -28,13 +28,14 @@ namespace Inkscape { #define SP_SHORTCUT_ALT_MASK (1 << 26) #define SP_SHORTCUT_MODIFIER_MASK (SP_SHORTCUT_SHIFT_MASK|SP_SHORTCUT_CONTROL_MASK|SP_SHORTCUT_ALT_MASK) + /* Returns true if action was performed */ bool sp_shortcut_invoke (unsigned int shortcut, Inkscape::UI::View::View *view); void sp_shortcut_init(); Inkscape::Verb * sp_shortcut_get_verb (unsigned int shortcut); unsigned int sp_shortcut_get_primary (Inkscape::Verb * verb); // Returns GDK_VoidSymbol if no shortcut is found. -char* sp_shortcut_get_label (unsigned int shortcut); // Returns the human readable form of the shortcut (or NULL), for example Shift+Ctrl+F. Free the returned string with g_free. +gchar* sp_shortcut_get_label (unsigned int shortcut); // Returns the human readable form of the shortcut (or NULL), for example Shift+Ctrl+F. Free the returned string with g_free. void sp_shortcut_set(unsigned int const shortcut, Inkscape::Verb *const verb, bool const is_primary, bool const is_user_set=false); void sp_shortcut_unset(unsigned int const shortcut); void sp_shortcut_add_to_file(char const *action, unsigned int const shortcut); @@ -48,6 +49,8 @@ void sp_shortcut_file_export(); bool sp_shortcut_file_import(); void sp_shortcut_file_import_do(char const *importname); void sp_shortcut_file_export_do(char const *exportname); +GtkAccelGroup *sp_shortcut_get_accel_group(); +void sp_shortcut_add_accelerator(GtkWidget *item, unsigned int const shortcut); #endif diff --git a/src/splivarot.cpp b/src/splivarot.cpp index 7cbd92eeb..8ce9a012b 100644 --- a/src/splivarot.cpp +++ b/src/splivarot.cpp @@ -307,7 +307,10 @@ sp_selected_path_boolop(SPDesktop *desktop, bool_op bop, const unsigned int verb originaux[1]->ConvertWithBackData(1.0); - originaux[1]->Fill(theShape, 1,false,false,false); //do not closeIfNeeded + if ((originaux[1]->pts.size() == 2) && originaux[1]->pts[0].isMoveTo && !originaux[1]->pts[1].isMoveTo) + originaux[1]->Fill(theShape, 1,false,true,false); // see LP Bug 177956 + else + originaux[1]->Fill(theShape, 1,false,false,false); //do not closeIfNeeded theShapeB->ConvertToShape(theShape, fill_justDont); // fill_justDont doesn't computes winding numbers diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 8fd8eb4e3..f3c3b8473 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -84,6 +84,7 @@ set(ui_SRC widget/entry.cpp widget/filter-effect-chooser.cpp widget/frame.cpp + widget/gimpcolorwheel.c widget/gimpspinscale.c widget/imageicon.cpp widget/imagetoggler.cpp @@ -212,6 +213,7 @@ set(ui_SRC widget/filter-effect-chooser.h widget/frame.h widget/gimpspinscale.h + widget/gimpcolorwheel.h widget/imageicon.h widget/imagetoggler.h widget/labelled.h diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 50f30e8f4..7dc94c92f 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -1201,7 +1201,6 @@ void FilterEffectsDialog::FilterModifier::on_change_selection() { Inkscape::Selection *selection = sp_desktop_selection (SP_ACTIVE_DESKTOP); update_selection(selection); - update_filters(); } void FilterEffectsDialog::FilterModifier::on_modified_selection( guint flags ) @@ -2668,7 +2667,7 @@ void FilterEffectsDialog::update_primitive_infobox() break; case(NR_FILTER_SPECULARLIGHTING): _infobox_icon.set_from_icon_name("feSpecularLighting-icon", Gtk::ICON_SIZE_DIALOG); - _infobox_desc.set_markup(_("The feDiffuseLighting and <b>feSpecularLighting</b> filter primitives create \"embossed\" shadings. The input's alpha channel is used to provide depth information: higher opacity areas are raised toward the viewer and lower opacity areas recede away from the viewer.")); + _infobox_desc.set_markup(_("The <b>feDiffuseLighting</b> and <b>feSpecularLighting</b> filter primitives create \"embossed\" shadings. The input's alpha channel is used to provide depth information: higher opacity areas are raised toward the viewer and lower opacity areas recede away from the viewer.")); break; case(NR_FILTER_TILE): _infobox_icon.set_from_icon_name("feTile-icon", Gtk::ICON_SIZE_DIALOG); diff --git a/src/ui/dialog/floating-behavior.cpp b/src/ui/dialog/floating-behavior.cpp index ba81c6d47..eab5f9d8f 100644 --- a/src/ui/dialog/floating-behavior.cpp +++ b/src/ui/dialog/floating-behavior.cpp @@ -12,6 +12,7 @@ #include <gtkmm/dialog.h> #include <gtkmm/stock.h> +#include <glibmm/main.h> #include <gtk/gtk.h> #include "floating-behavior.h" diff --git a/src/ui/dialog/font-substitution.cpp b/src/ui/dialog/font-substitution.cpp index 07e73cec8..f0112f1d8 100644 --- a/src/ui/dialog/font-substitution.cpp +++ b/src/ui/dialog/font-substitution.cpp @@ -37,6 +37,8 @@ #include "libnrtype/FontFactory.h" #include "libnrtype/font-instance.h" +#include <glibmm/regex.h> + namespace Inkscape { namespace UI { namespace Dialog { diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 57f815730..c162681e7 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -18,6 +18,7 @@ #endif #include <glibmm/i18n.h> +#include <glibmm/miscutils.h> #include "inkscape-preferences.h" #include <gtkmm/main.h> @@ -1288,7 +1289,7 @@ void InkscapePreferences::initPageBehavior() _page_mask.add_group_header( _("Before applying")); _mask_grouping_none.init( _("Do not group clipped/masked objects"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE, true, 0); - _mask_grouping_separate.init( _("Enclose every clipped/masked object in its own group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_SEPARATE, false, &_mask_grouping_none); + _mask_grouping_separate.init( _("Put every clipped/masked object in its own group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_SEPARATE, false, &_mask_grouping_none); _mask_grouping_all.init( _("Put all clipped/masked objects into one group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_ALL, false, &_mask_grouping_none); _page_mask.add_line(true, "", _mask_grouping_none, "", diff --git a/src/ui/dialog/layers.cpp b/src/ui/dialog/layers.cpp index fdc33b2a6..dd147d00f 100644 --- a/src/ui/dialog/layers.cpp +++ b/src/ui/dialog/layers.cpp @@ -20,6 +20,7 @@ #include <gtkmm/separatormenuitem.h> #include <glibmm/i18n.h> +#include <glibmm/main.h> #include "desktop.h" #include "desktop-style.h" diff --git a/src/ui/dialog/swatches.cpp b/src/ui/dialog/swatches.cpp index 43b88e5c6..71fee342a 100644 --- a/src/ui/dialog/swatches.cpp +++ b/src/ui/dialog/swatches.cpp @@ -23,6 +23,7 @@ #include <glibmm/i18n.h> #include <glibmm/main.h> +#include <glibmm/timer.h> #include <gdkmm/pixbuf.h> #include "color-item.h" diff --git a/src/ui/widget/dock-item.cpp b/src/ui/widget/dock-item.cpp index b9cc50845..8d960ddc3 100644 --- a/src/ui/widget/dock-item.cpp +++ b/src/ui/widget/dock-item.cpp @@ -18,6 +18,7 @@ #include <gtkmm/icontheme.h> #include <gtkmm/stockitem.h> +#include <glibmm/exceptionhandler.h> namespace Inkscape { namespace UI { diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 9e078cabb..2a816adf4 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -775,6 +775,10 @@ static void sp_desktop_widget_dispose(GObject *object) { SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (object); + if (dtw == NULL) { + return; + } + UXManager::getInstance()->delTrack(dtw); if (dtw->desktop) { diff --git a/src/widgets/font-selector.cpp b/src/widgets/font-selector.cpp index 013ad9e94..59fe25fa1 100644 --- a/src/widgets/font-selector.cpp +++ b/src/widgets/font-selector.cpp @@ -51,8 +51,6 @@ struct SPFontSelector NRNameList families; NRStyleList styles; - int familyidx; - int styleidx; gfloat fontsize; bool fontsize_dirty; font_instance *font; @@ -246,9 +244,7 @@ static void sp_font_selector_init(SPFontSelector *fsel) gtk_widget_show_all (fsel->size); - fsel->familyidx = 0; - fsel->styleidx = 0; - fsel->fontsize = 10.0; + fsel->fontsize = 18.0; fsel->fontsize_dirty = false; fsel->font = NULL; } @@ -290,8 +286,6 @@ static void sp_font_selector_family_select_row(GtkTreeSelection *selection, path = gtk_tree_model_get_path (model, &iter); gtk_tree_model_get (model, &iter, 1, &list, -1); - fsel->familyidx = gtk_tree_path_get_indices (path)[0]; - fsel->styleidx = 0; store = gtk_list_store_new (1, G_TYPE_STRING); @@ -318,7 +312,6 @@ static void sp_font_selector_style_select_row (GtkTreeSelection *selection, if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; path = gtk_tree_model_get_path (model, &iter); - fsel->styleidx = gtk_tree_path_get_indices (path)[0]; if (!fsel->block_emit) { diff --git a/src/widgets/icon.cpp b/src/widgets/icon.cpp index 8470e93db..7580d9602 100644 --- a/src/widgets/icon.cpp +++ b/src/widgets/icon.cpp @@ -24,6 +24,7 @@ #include <gtkmm/image.h> #include <gdkmm/pixbuf.h> #include <glibmm/fileutils.h> +#include <glibmm/miscutils.h> #include <2geom/transforms.h> #include "path-prefix.h" diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index a01f950a6..cc6d02ea8 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -133,115 +133,7 @@ static void sp_print_fontstyle( SPStyle *query ) { } #endif -/* - * Fill the font style combobox with the available font styles for the selected font family - * Set the selected style to that in font - */ -static void sp_text_fontstyle_populate(GObject *tbl, font_instance *font=NULL) -{ - - Ink_ComboBoxEntry_Action* act = INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontFamilyAction" ) ); - GtkTreeModel *model = ink_comboboxentry_action_get_model( act ); - gchar *current_font = ink_comboboxentry_action_get_active_text( act ); - if (!current_font) { - return; - } - - // If font list, take only first font in list - gchar** tokens = g_strsplit( current_font, ",", 0 ); - g_strstrip( tokens[0] ); - current_font = tokens[0]; - - // Get an iter to the selected font from the model data - // We cant get it from the combo, cause it might not have been created yet - gboolean found = false; - GtkTreeIter iter; - gboolean valid = gtk_tree_model_get_iter_first( model, &iter ); - while ( valid ) { - - // Get text from list entry - gchar* text = NULL; - gtk_tree_model_get( model, &iter, 0, &text, -1 ); // Column 0 - - // Check for match - if ( text && (strcmp( current_font, text ) == 0) ) { - found = true; - break; - } - valid = gtk_tree_model_iter_next( model, &iter ); - } - - g_strfreev( tokens ); - - Ink_ComboBoxEntry_Action* fontStyleAction = INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontStyleAction" ) ); - - gchar *current_style = ink_comboboxentry_action_get_active_text( fontStyleAction ); - - GtkListStore *store = GTK_LIST_STORE( ink_comboboxentry_action_get_model( fontStyleAction ) ); - gtk_list_store_clear ( store ); - - // Get the list of styles from the selected font. - GList *list = NULL; - - if (found) { - - // Use precompiled list if font-family on system. - gtk_tree_model_get (model, &iter, 1, &list, -1); - - } else { - - // Use generic list if font-family not on system. - static GList *glist = NULL; - if( glist == NULL ) { - glist = g_list_append (glist, (void*)"Normal"); - glist = g_list_append (glist, (void*)"Italic"); - glist = g_list_append (glist, (void*)"Bold"); - glist = g_list_append (glist, (void*)"Bold Italic"); - } - list = glist; - } - - for (GList *l=list; l; l = l->next) - { - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, 0, (char*)l->data, -1); - } - - // Select the style in the combo that best matches font - if (font && list) { - - unsigned int index = sp_font_selector_get_best_style(font, list); - - Ink_ComboBoxEntry_Action* fontStyleAction = - INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontStyleAction" ) ); - model = ink_comboboxentry_action_get_model( fontStyleAction ); - GtkTreePath *path_c = gtk_tree_path_new (); - gtk_tree_path_append_index (path_c, index); - gtk_tree_model_get_iter(model, &iter, path_c); - gchar *name; - gtk_tree_model_get (model, &iter, 0, &name, -1); - ink_comboboxentry_action_set_active_text( fontStyleAction, name ); - - } else if (current_style) { - ink_comboboxentry_action_set_active_text( fontStyleAction, current_style ); - } -} - // Font family -// -// In most cases we should just be able to set the new family name -// but there may be cases where a font family doesn't follow the -// standard naming pattern. To handle those cases, we do a song and -// dance to use Pango to find the best match. To do that we start -// with the old "fontSpec" (which is the returned string from -// pango_font_description_to_string() with the size unset). This -// has the form "[family-list] [style-options]" where the -// family-list is a comma separated list of font-family names -// (optionally terminated by a comma). An example would be -// "DejaVu Sans, Sans Bold". Only a "fontSpec" containing a -// single font-family will work with Pango's best match routine. -// If we can't obtain a good "fontSpec", we then resort to blindly -// changing the font-family. static void sp_text_fontfamily_value_changed( Ink_ComboBoxEntry_Action *act, GObject *tbl ) { #ifdef DEBUG_TEXT @@ -252,156 +144,45 @@ static void sp_text_fontfamily_value_changed( Ink_ComboBoxEntry_Action *act, GOb // quit if run by the _changed callbacks if (g_object_get_data(G_OBJECT(tbl), "freeze")) { - return; - } - g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); - - gchar *family = ink_comboboxentry_action_get_active_text( act ); -#ifdef DEBUG_TEXT - std::cout << " New family: " << family << std::endl; -#endif - - // First try to get the old font spec from the stored value - SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); - int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); - - Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : ""; -#ifdef DEBUG_TEXT - std::cout << " fontSpec from query :" << fontSpec << ":" << std::endl; -#endif - - // If that didn't work, try to get font spec from style - if (fontSpec.empty()) { - - // Must query all to fill font-family, font-style, font-weight, font-specification - sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); - sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); - sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); - - // Construct a new font specification if it does not yet exist - font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query); - if( fontFromStyle ) { - fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle); - fontFromStyle->Unref(); - } - -#ifdef DEBUG_TEXT - std::cout << " fontSpec empty, try from style" << std::endl; - std::cout << " from style :" << fontSpec << ":" << std::endl; - sp_print_font( query ); -#endif - - } - - // And if that didn't work use default. DO WE REALLY WANT TO DO THIS? - if ( fontSpec.empty() ) { - - sp_style_read_from_prefs(query, "/tools/text"); - - // Construct a new font specification if it does not yet exist - font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query); - if ( fontFromStyle ) { - fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle); - fontFromStyle->Unref(); - } - #ifdef DEBUG_TEXT - std::cout << " fontSpec empty, trying from prefs" << std::endl; - std::cout << " from prefs :" << fontSpec << ":" << std::endl; - sp_print_font( query ); + std::cout << "sp_text_fontfamily_value_changed: frozen... return" << std::endl; + std::cout << "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n" << std::endl; #endif + return; } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); - // Now we have a font specification, replace family. - Glib::ustring newFontSpec = ""; - SPCSSAttr *css = sp_repr_css_attr_new (); - - if (!fontSpec.empty()) newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family); - -#ifdef DEBUG_TEXT - std::cout << " New FontSpec from ReplaceFontSpecificationFamily :" << newFontSpec << ":" << std::endl; -#endif - - if (!fontSpec.empty() && !newFontSpec.empty() ) { - - if (fontSpec != newFontSpec) { - - font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str()); - - if (font) { - sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str()); - - // Set all the these just in case they were altered when finding the best - // match for the new family and old style... Unnecessary? - - gchar c[256]; - - font->Family(c, 256); - - sp_repr_css_set_property (css, "font-family", c); - - font->Attribute( "weight", c, 256); - sp_repr_css_set_property (css, "font-weight", c); - - font->Attribute("style", c, 256); - sp_repr_css_set_property (css, "font-style", c); - - font->Attribute("stretch", c, 256); - sp_repr_css_set_property (css, "font-stretch", c); - - font->Attribute("variant", c, 256); - sp_repr_css_set_property (css, "font-variant", c); - - font->Unref(); - - // Set the list of font styles - sp_text_fontstyle_populate(tbl); - - } else { - g_warning(_("Failed to find font matching: %s\n"), newFontSpec.c_str()); - } - } - } else { - - // Either old font does not exist on system or ReplaceFontSpecificationFamily() failed. - // Blindly fall back to setting the family to text in the font-family chooser. + Glib::ustring new_family = ink_comboboxentry_action_get_active_text( act ); + // TODO: Think about how to handle handle multiple selections. While + // the font-family may be the same for all, the styles might be different. + // See: TextEdit::onApply() for example of looping over selected items. + Inkscape::FontLister* fontlister = Inkscape::FontLister::get_instance(); #ifdef DEBUG_TEXT - std::cout << " Failed to find new font, blindly setting family: " << family << std::endl; + std::cout << " Old family: " << fontlister->get_font_family() << std::endl; + std::cout << " New family: " << new_family << std::endl; + std::cout << " Old active: " << fontlister->get_font_family_row() << std::endl; + std::cout << " New active: " << act->active << std::endl; #endif - sp_repr_css_set_property (css, "-inkscape-font-specification", family); - sp_repr_css_set_property (css, "font-family", family); - } + if( new_family.compare( fontlister->get_font_family() ) != 0 ) { - // If querying returned nothing, update default style. - if (result_fontspec == QUERY_STYLE_NOTHING) - { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->mergeStyle("/tools/text/style", css); - //sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace through a verb - } - else - { - sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true); - } + std::pair<Glib::ustring,Glib::ustring> ui = fontlister->set_font_family( act->active ); + // active text set in sp_text_toolbox_selection_changed() - sp_style_unref(query); + SPCSSAttr *css = sp_repr_css_attr_new (); + fontlister->set_css( css ); - g_free (family); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + sp_desktop_set_style (desktop, css, true, true); // Results in selection change called twice. + sp_repr_css_attr_unref (css); - // Save for undo - if (result_fontspec != QUERY_STYLE_NOTHING) { - DocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT, + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, _("Text: Change font family")); } - sp_repr_css_attr_unref (css); // unfreeze g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); - // focus to canvas (not useful if you want to choose font for your text) - //gtk_widget_grab_focus (GTK_WIDGET((SP_ACTIVE_DESKTOP)->canvas)); - #ifdef DEBUG_TEXT std::cout << "sp_text_toolbox_fontfamily_changes: exit" << std::endl; std::cout << "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" << std::endl; @@ -473,7 +254,6 @@ static void sp_text_fontsize_value_changed( Ink_ComboBoxEntry_Action *act, GObje /* * Font style */ -//static void sp_text_fontstyle_value_changed( EgeSelectOneAction *act, GObject *tbl ) static void sp_text_fontstyle_value_changed( Ink_ComboBoxEntry_Action *act, GObject *tbl ) { // quit if run by the _changed callbacks @@ -482,95 +262,26 @@ static void sp_text_fontstyle_value_changed( Ink_ComboBoxEntry_Action *act, GObj } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); - // First query font-specification, this is the most complete font face description. - SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); - int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); + Glib::ustring new_style = ink_comboboxentry_action_get_active_text( act ); - // font_specification will not be set unless defined explicitily on a tspan. - // This should be fixed! - Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : ""; + Inkscape::FontLister* fontlister = Inkscape::FontLister::get_instance(); - if (fontSpec.empty()) { - // Construct a new font specification if it does not yet exist - // Must query font-family, font-style, font-weight, to find correct font face. - sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); - sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); - sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + if( new_style.compare( fontlister->get_font_style() ) != 0 ) { - font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query); - if ( fontFromStyle ) { - fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle); - fontFromStyle->Unref(); - } - } + fontlister->set_font_style( new_style ); + // active text set in sp_text_toolbox_seletion_changed() - SPCSSAttr *css = sp_repr_css_attr_new (); + SPCSSAttr *css = sp_repr_css_attr_new (); + fontlister->set_css( css ); - Glib::ustring current_style = ink_comboboxentry_action_get_active_text( act ); - Glib::ustring fontFamily = ""; + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + sp_desktop_set_style (desktop, css, true, true); + sp_repr_css_attr_unref (css); - if (query->text->font_family.set) { - fontFamily = query->text->font_family.value; - } else { - // if the font_family is not set, get it from the font family combo instead - Ink_ComboBoxEntry_Action* act = INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontFamilyAction" ) ); - fontFamily = ink_comboboxentry_action_get_active_text( act ); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Text: Change font style")); } - font_instance *font = (font_factory::Default())->FaceFromUIStrings (fontFamily.c_str(), current_style.c_str()); - - if (font) { - - gchar c[256]; - - font->Attribute( "weight", c, 256); - sp_repr_css_set_property (css, "font-weight", c); - - font->Attribute("style", c, 256); - sp_repr_css_set_property (css, "font-style", c); - - font->Attribute("stretch", c, 256); - sp_repr_css_set_property (css, "font-stretch", c); - - font->Attribute("variant", c, 256); - sp_repr_css_set_property (css, "font-variant", c); - - font->Unref(); - font = NULL; - - } else { - - // Font not found on system, blindly update style - // Options match choices in sp_text_fontstyle_populate - sp_repr_css_set_property (css, "font-weight", "normal"); - sp_repr_css_set_property (css, "font-style", "normal" ); - if( current_style.find("Bold") != Glib::ustring::npos ) { - sp_repr_css_set_property (css, "font-weight", "bold"); - } - if( current_style.find("Italic") != Glib::ustring::npos ) { - sp_repr_css_set_property (css, "font-style", "italic"); - } - } - - // If querying returned nothing, update default style. - if (result_fontspec == QUERY_STYLE_NOTHING) - { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->mergeStyle("/tools/text/style", css); - } - - sp_style_unref(query); - - // Do we need to update other CSS values? - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - sp_desktop_set_style (desktop, css, true, true); - if (result_fontspec != QUERY_STYLE_NOTHING) { - DocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT, - _("Text: Change font style")); - } - - sp_repr_css_attr_unref (css); - g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } @@ -1145,6 +856,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ if (g_object_get_data(G_OBJECT(tbl), "freeze")) { #ifdef DEBUG_TEXT std::cout << " Frozen, returning" << std::endl; + std::cout << "sp_text_toolbox_selection_changed: exit " << count << std::endl; std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; std::cout << std::endl; #endif @@ -1152,15 +864,21 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); - // Update font list, but only if widget already created. Ink_ComboBoxEntry_Action* fontFamilyAction = INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontFamilyAction" ) ); + Ink_ComboBoxEntry_Action* fontStyleAction = + INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontStyleAction" ) ); + + Inkscape::FontLister* fontlister = Inkscape::FontLister::get_instance(); + fontlister->update_font_list( sp_desktop_document( SP_ACTIVE_DESKTOP )); + fontlister->selection_update(); + + // Update font list, but only if widget already created. if( fontFamilyAction->combobox != NULL ) { - Inkscape::FontLister* fontlister = Inkscape::FontLister::get_instance(); - fontlister->update_font_list( sp_desktop_document( SP_ACTIVE_DESKTOP )); + ink_comboboxentry_action_set_active_text( fontFamilyAction, fontlister->get_font_family().c_str(), fontlister->get_font_family_row() ); + ink_comboboxentry_action_set_active_text( fontStyleAction, fontlister->get_font_style().c_str() ); } - // Only flowed text can be justified, only normal text can be kerned... // Find out if we have flowed text now so we can use it several places gboolean isFlow = false; @@ -1207,6 +925,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); #ifdef DEBUG_TEXT std::cout << " text_style_from_prefs: toolbar already set" << std:: endl; + std::cout << "sp_text_toolbox_selection_changed: exit " << count << std::endl; std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; std::cout << std::endl; #endif @@ -1225,15 +944,6 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ // If we have valid query data for text (font-family, font-specification) set toolbar accordingly. if (query->text) { - // Font family - if( query->text->font_family.value ) { - gchar *fontFamily = query->text->font_family.value; - - Ink_ComboBoxEntry_Action* fontFamilyAction = - INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontFamilyAction" ) ); - ink_comboboxentry_action_set_active_text( fontFamilyAction, fontFamily ); - } - // Size (average of text selected) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int unit = prefs->getInt("/options/font/unitType", SP_CSS_UNIT_PT); @@ -1247,16 +957,17 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ Ink_ComboBoxEntry_Action* fontSizeAction = INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontSizeAction" ) ); + + // Freeze to ignore callbacks. + //g_object_freeze_notify( G_OBJECT( fontSizeAction->combobox ) ); sp_text_set_sizes(GTK_LIST_STORE(ink_comboboxentry_action_get_model(fontSizeAction)), unit); + //g_object_thaw_notify( G_OBJECT( fontSizeAction->combobox ) ); + ink_comboboxentry_action_set_active_text( fontSizeAction, os.str().c_str() ); Glib::ustring tooltip = Glib::ustring::format(_("Font size"), " (", sp_style_get_css_unit_string(unit), ")"); ink_comboboxentry_action_set_tooltip ( fontSizeAction, tooltip.c_str()); - // Font styles - font_instance *font = font_factory::Default()->FaceFromStyle(query); - sp_text_fontstyle_populate(tbl, font); - // Superscript gboolean superscriptSet = ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) && @@ -1438,6 +1149,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ } #ifdef DEBUG_TEXT + std::cout << "sp_text_toolbox_selection_changed: exit " << count << std::endl; std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; std::cout << std::endl; #endif @@ -1457,41 +1169,47 @@ sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl) sp_text_toolbox_selection_changed (NULL, tbl); } +// TODO: possibly share with font-selector by moving most code to font-lister (passing family name) +static void sp_text_toolbox_select_cb( GtkEntry* entry, GtkEntryIconPosition /*position*/, GdkEvent /*event*/, gpointer /*data*/ ) { -/* Recursively extract all "font-family" attributes from a document. */ -void -sp_text_toolbox_get_font_list_in_doc_recursive (SPObject *r, std::list<Glib::ustring> *l) -{ - if (!r) { - return; - } + Glib::ustring family = gtk_entry_get_text ( entry ); + //std::cout << "text_toolbox_missing_font_cb: selecting: " << family << std::endl; - const gchar *style = r->getRepr()->attribute("style"); - if( style != NULL ) { - //std::cout << style << std::endl; - std::vector<Glib::ustring> tokens = Glib::Regex::split_simple(";", style ); - for( size_t i=0; i < tokens.size(); ++i ) { - Glib::ustring token = tokens[i]; - size_t found = token.find("font-family:"); - if( found != Glib::ustring::npos ) { - // Remove "font-family:" - token.erase(found,12); - // Remove any leading single or double quote - if( token[0] == '\'' || token[0] == '"' ) { - token.erase(0,1); - } - // Remove any trailing single or double quote - if( token[token.length()-1] == '\'' || token[token.length()-1] == '"' ) { - token.erase(token.length()-1); - } - l->push_back( token ); - } - } - } + // Get all items with matching font-family set (not inherited!). + GSList *selectList = NULL; + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPDocument *document = sp_desktop_document( desktop ); + GSList *allList = get_all_items(NULL, document->getRoot(), desktop, false, false, true, NULL); + for (GSList *i = allList; i != NULL; i = i->next) { + + SPItem *item = SP_ITEM(i->data); + SPStyle *style = item->style; + + if (style && style->text) { + + Glib::ustring family_style; + if (style->text->font_family.set) { + family_style = style->text->font_family.value; + //std::cout << " family style from font_family: " << family_style << std::endl; + } + else if (style->text->font_specification.set) { + family_style = style->text->font_specification.value; + //std::cout << " family style from font_spec: " << family_style << std::endl; + } - for (SPObject *child = r->firstChild(); child; child = child->getNext()) { - sp_text_toolbox_get_font_list_in_doc_recursive( child, l ); + if (family_style.compare( family ) == 0 ) { + //std::cout << " found: " << item->getId() << std::endl; + selectList = g_slist_prepend (selectList, item); + } } + } + + // Update selection + Inkscape::Selection *selection = sp_desktop_selection (desktop ); + selection->clear(); + //std::cout << " list length: " << g_slist_length ( selectList ) << std::endl; + selection->setList(selectList); } @@ -1526,8 +1244,16 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje (gpointer)font_lister_separator_func, GTK_WIDGET(desktop->canvas)); // Focus widget ink_comboboxentry_action_popup_enable( act ); // Enable entry completion + + gchar *const info = _("Select all text with this font-family"); + ink_comboboxentry_action_set_info( act, info ); // Show selection icon + ink_comboboxentry_action_set_info_cb( act, (gpointer)sp_text_toolbox_select_cb ); + gchar *const warning = _("Font not found on system"); - ink_comboboxentry_action_set_warning( act, warning ); // Show icon with tooltip if missing font + ink_comboboxentry_action_set_warning( act, warning ); // Show icon w/ tooltip if font missing + ink_comboboxentry_action_set_warning_cb( act, (gpointer)sp_text_toolbox_select_cb ); + + //ink_comboboxentry_action_set_warning_callback( act, sp_text_fontfamily_select_all ); ink_comboboxentry_action_set_altx_name( act, "altx-text" ); // Set Alt-X keyboard shortcut g_signal_connect( G_OBJECT(act), "changed", G_CALLBACK(sp_text_fontfamily_value_changed), holder ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); @@ -1577,18 +1303,20 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje /* Font styles */ { - GtkListStore* model_style = gtk_list_store_new( 1, G_TYPE_STRING ); + Inkscape::FontLister* fontlister = Inkscape::FontLister::get_instance(); + Glib::RefPtr<Gtk::ListStore> store = fontlister->get_style_list(); + GtkListStore* model_style = store->gobj(); - Ink_ComboBoxEntry_Action* act = ink_comboboxentry_action_new( "TextFontStyleAction", + Ink_ComboBoxEntry_Action* act = ink_comboboxentry_action_new( "TextFontStyleAction", _("Font Style"), _("Font style"), NULL, GTK_TREE_MODEL(model_style), 12, // Width in characters - 0, // Extra list width - NULL, // Cell layout - NULL, // Separator - GTK_WIDGET(desktop->canvas)); // Focus widget + 0, // Extra list width + NULL, // Cell layout + NULL, // Separator + GTK_WIDGET(desktop->canvas)); // Focus widget g_signal_connect( G_OBJECT(act), "changed", G_CALLBACK(sp_text_fontstyle_value_changed), holder ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); |
