/* Screem: skel-plugin.c * * Copyright (C) 2004 David A Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include "screem-skel-plugin.h" /* setup your plugin here */ #include "support.h" /* plugin name should only include a-zA-Z */ static const gchar *plugin_name = "ScreemCSSWizard"; static const gchar *authors[] = { "David A Knight ", NULL }; static const gchar *displayed_name = N_( "Screem CSS Wizard" ); static const gchar *description = N_( "A wizard for inserting CSS selectors and properties" ); static const gchar *version = "2.0.0"; /* add any per instance data items here */ struct ScreemSkelPluginPrivate { gpointer dummy; }; enum { ANYWHERE = 0, SOMEWHERE, CHILD_OF, AFTER }; static void css_selector_wizard_display( GtkAction *action, gpointer user_data ); static void create_tag_menu( ScreemPlugin *plugin, GladeXML *xml ); static void create_action_menu( GladeXML *xml ); static gchar *create_styles( GladeXML *xml ); static gchar *create_selector( GtkWidget *widget ); static void add_page( GtkWidget *widget, const gchar *label_txt ); void css_selector_next_change( GtkComboBox *omenu ); void css_selector_tag_change( GtkWidget *widget ); void css_wizard_color_set( GtkWidget *widget, GtkColorButton *cp ); /** * setup: * * this function will be called once for each window, * you should add any actions / ui here, eg. * * screem_plugin_add_action( plugin, name, label, tip, stock_id, * callback, error ); * screem_plugin_add_menu( plugin, path, action, error ); * screem_plugin_add_toolbar( plugin, path, action, error ); * * * to insert text into the current page being edited your callbacks * should make use of * screem_plugin_get_cursor_position( plugin ) * screem_plugin_set_cursor_position( plugin, pos ) * screem_plugin_insert( plugin, pos, text, length, indent ) * **/ static gboolean setup( ScreemPlugin *plugin ) { GError *error; gboolean ret; error = NULL; ret = screem_plugin_add_interface( plugin, "CSSSelectorWizard", _( "CSS Selector" ), _( "A wizard to help you construct selectors for applying css properties to" ), GTK_STOCK_EXECUTE, G_CALLBACK( css_selector_wizard_display ), &error ); if( ! ret ) { g_print( "Add interface error: %s\n", error->message ); g_error_free( error ); } return ret; } /** * cleanup: * * this function will be called once for each window when * it is closed, you should cleanup any data items you * have in ScreemSkelPluginPrivate here **/ static void cleanup( ScreemSkelPluginPrivate *priv ) { } static void create_tag_menu( ScreemPlugin *plugin, GladeXML *xml ) { GtkWidget *widget; GtkWidget *entry; GtkListStore *store; GtkTreeIter it; ScreemPage *page; ScreemDTD *dtd; const GSList *elements; const ScreemDTDElement *elem; const gchar *name; gboolean set; GtkEntryCompletion *comp; widget = glade_xml_get_widget( xml, "tag_menu" ); entry = GTK_BIN( widget )->child; page = screem_plugin_get_current_document( plugin ); if( page ) { comp = gtk_entry_completion_new(); store = gtk_list_store_new( 1, G_TYPE_STRING ); dtd = screem_page_get_dtd( page ); elements = screem_dtd_get_elements( dtd ); name = ""; set = FALSE; if( elements ) { set = TRUE; elem = (const ScreemDTDElement*)elements->data; name = screem_dtd_element_get_name( elem ); } gtk_entry_set_text( GTK_ENTRY( entry ), name ); while( elements ) { elem = (const ScreemDTDElement*)elements->data; name = screem_dtd_element_get_name( elem ); gtk_list_store_append( store, &it ); gtk_list_store_set( store, &it, 0, name, -1 ); elements = elements->next; } g_signal_connect_swapped( G_OBJECT( entry ), "changed", G_CALLBACK( css_selector_tag_change ), widget ); gtk_entry_completion_set_model( GTK_ENTRY_COMPLETION( comp ), GTK_TREE_MODEL( store ) ); gtk_entry_completion_set_text_column( GTK_ENTRY_COMPLETION( comp ), 0 ); gtk_entry_set_completion( GTK_ENTRY( entry ), comp ); gtk_combo_box_set_model( GTK_COMBO_BOX( widget ), GTK_TREE_MODEL( store ) ); gtk_combo_box_entry_set_text_column( GTK_COMBO_BOX_ENTRY( widget ), 0 ); if( set ) { gtk_combo_box_set_active( GTK_COMBO_BOX( widget ), 0 ); } g_object_unref( comp ); } } static void create_action_menu( GladeXML *xml ) { GtkWidget *widget; GtkListStore *store; GtkTreeIter it; gint i; const gchar *actions[] = { N_( "activated" ), "active", N_( "hovered" ), "hover", N_( "focused" ), "focus", NULL }; widget = glade_xml_get_widget( xml, "action_menu" ); store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING ); for( i = 0; actions[ i ]; i += 2 ) { gtk_list_store_append( store, &it ); gtk_list_store_set( store, &it, 0, gettext( actions[ i ] ), 1, actions[ i + 1 ], -1 ); } gtk_combo_box_set_model( GTK_COMBO_BOX( widget ), GTK_TREE_MODEL( store ) ); gtk_combo_box_set_active( GTK_COMBO_BOX( widget ), 0 ); } static void add_page( GtkWidget *widget, const gchar *label_txt ) { GladeXML *xml; GtkWidget *notebook; GtkWidget *label; ScreemPlugin *plugin; xml = glade_get_widget_tree( widget ); widget = glade_xml_get_widget( xml, "match_box" ); notebook = g_object_get_data( G_OBJECT( widget ), "notebook" ); plugin = g_object_get_data( G_OBJECT( widget ), "plugin" ); xml = glade_xml_new( GLADE_PATH"/css-wizard.glade", "match_box", NULL ); widget = glade_xml_get_widget( xml, "match_box" ); g_object_set_data( G_OBJECT( widget ), "notebook", notebook ); g_object_set_data( G_OBJECT( widget ), "plugin", plugin ); label = gtk_label_new( label_txt ); create_tag_menu( plugin, xml ); create_action_menu( xml ); css_selector_tag_change( widget ); widget = glade_xml_get_widget( xml, "hyperlink_menu" ); gtk_combo_box_set_active( GTK_COMBO_BOX( widget ), 0 ); widget = glade_xml_get_widget( xml, "location_menu" ); gtk_combo_box_set_active( GTK_COMBO_BOX( widget ), 0 ); widget = glade_xml_get_widget( xml, "class_entry" ); screem_gtk_add_history( widget ); widget = glade_xml_get_widget( xml, "id_entry" ); screem_gtk_add_history( widget ); widget = glade_xml_get_widget( xml, "match_box" ); gtk_widget_show( widget ); gtk_widget_show( label ); gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), widget, label ); glade_xml_signal_autoconnect( xml ); } static gchar *create_selector( GtkWidget *widget ) { GladeXML *xml; GtkWidget *notebook; gint pages; gint i; gint item; GString *pattern; gchar *ret; GtkTreeModel *model; GtkTreeIter it; gchar *action; pattern = g_string_new( "\n" ); notebook = g_object_get_data( G_OBJECT( widget ), "notebook" ); pages = gtk_notebook_get_n_pages( GTK_NOTEBOOK( notebook ) ); for( i = 0; i < pages; ) { widget = gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), i ); xml = glade_get_widget_tree( widget ); widget = glade_xml_get_widget( xml, "action_menu" ); if( GTK_WIDGET_IS_SENSITIVE( widget ) ) { /* apply action */ model = gtk_combo_box_get_model( GTK_COMBO_BOX( widget ) ); gtk_combo_box_get_active_iter( GTK_COMBO_BOX( widget ), &it ); gtk_tree_model_get( model, &it, 1, &action, -1 ); g_string_prepend( pattern, action ); g_free( action ); g_string_prepend( pattern, ":" ); } widget = glade_xml_get_widget( xml, "hyperlink_menu" ); if( GTK_WIDGET_IS_SENSITIVE( widget ) ) { item = gtk_combo_box_get_active( GTK_COMBO_BOX( widget ) ); if( item == 0 ) { g_string_prepend( pattern, ":link" ); } else { g_string_prepend( pattern, ":visited" ); } } widget = glade_xml_get_widget( xml, "id_entry" ); if( GTK_WIDGET_IS_SENSITIVE( widget ) ) { /* match id */ widget = GTK_BIN( widget )->child; g_string_prepend( pattern, gtk_entry_get_text( GTK_ENTRY( widget ) ) ); g_string_prepend_c( pattern, '#' ); } widget = glade_xml_get_widget( xml, "class_entry" ); if( GTK_WIDGET_IS_SENSITIVE( widget ) ) { /* match class */ widget = GTK_BIN( widget )->child; g_string_prepend( pattern, gtk_entry_get_text( GTK_ENTRY( widget ) ) ); g_string_prepend_c( pattern, '.' ); } widget = glade_xml_get_widget( xml, "tag_menu" ); if( GTK_WIDGET_IS_SENSITIVE( widget ) ) { /* match tag */ widget = GTK_BIN( widget )->child; g_string_prepend( pattern, gtk_entry_get_text( GTK_ENTRY( widget ) ) ); } i ++; if( i != pages ) { widget = glade_xml_get_widget( xml, "location_menu" ); item = gtk_combo_box_get_active( GTK_COMBO_BOX( widget ) ); switch( item ) { case SOMEWHERE: g_string_prepend_c( pattern, ' ' ); break; case CHILD_OF: g_string_prepend( pattern, " > " ); break; case AFTER: g_string_prepend( pattern, " + " ); break; default: break; } } } ret = pattern->str; g_string_free( pattern, FALSE ); return ret; } static gchar *create_styles( GladeXML *xml ) { GString *str; gchar *ret; GtkWidget *widget; const gchar *fields[] = { /* fonts */ "font-family", "font-style", "font-variant", "font-weight", "font-size", "font-size-adjust", "font-stretch", "color", /* background */ "background-color", "background-image", "background-repeat", "background-attachment", "background-position", /* text */ "word-spacing", "letter-spacing", "text-decoration", "vertical-align", "text-transform", "text-align", "text-indent", "line-height", /* box */ "margin-top", "margin-left", "margin-bottom", "margin-right", "padding-top", "padding-left", "padding-bottom", "padding-right", /* border */ "border-top-color", "border-left-color", "border-bottom-color", "border-right-color", "border-top-width", "border-left-width", "border-bottom-width", "border-right-width", "border-top-style", "border-left-style", "border-bottom-style", "border-right-style", /* size / placement */ "width", "height", "float", "clear", /* classification */ "display", "white-space", "list-style-type", "list-style-image", "list-style-position" }; static const guint n_fields = G_N_ELEMENTS( fields ); guint i; const gchar *field; const gchar *value; gchar *uri; str = g_string_new( "" ); for( i = 0; i < n_fields; ++ i ) { field = fields[ i ]; widget = glade_xml_get_widget( xml, field ); uri = NULL; if( GTK_IS_FILE_CHOOSER_BUTTON( widget ) ) { uri = gtk_file_chooser_get_uri( GTK_FILE_CHOOSER( widget ) ); value = uri; if( value && *value ) { uri = g_strconcat( "url( ", value, ")", NULL ); g_free( (gchar*)value); value = uri; } } else { if( GTK_IS_COMBO_BOX_ENTRY( widget ) ) { widget = GTK_BIN( widget )->child; } value = gtk_entry_get_text( GTK_ENTRY( widget ) ); } if( value && *value ) { g_string_append_printf( str, "\t%s: %s;\n", field, value ); } g_free( uri ); } if( str->len > 0 ) { g_string_prepend( str, "{\n" ); g_string_append( str, "}\n" ); } ret = str->str; g_string_free( str, FALSE ); return ret; } static void css_selector_wizard_display( GtkAction *action, gpointer user_data ) { ScreemPlugin *plugin; ScreemPage *page; GtkWidget *widget; GtkWidget *notebook; GladeXML *xml; gint button; gchar *pattern; gchar *styles; plugin = SCREEM_PLUGIN( user_data ); page = screem_plugin_get_current_document( plugin ); if( page ) { xml = glade_xml_new( GLADE_PATH"/css-wizard.glade", "csspattern", NULL ); widget = glade_xml_get_widget( xml, "match_box" ); notebook = glade_xml_get_widget( xml, "notebook" ); g_object_set_data( G_OBJECT( widget ), "notebook", notebook ); g_object_set_data( G_OBJECT( widget ), "plugin", plugin ); create_tag_menu( plugin, xml ); create_action_menu( xml ); widget = glade_xml_get_widget( xml, "hyperlink_menu" ); gtk_combo_box_set_active( GTK_COMBO_BOX( widget ), 0 ); widget = glade_xml_get_widget( xml, "location_menu" ); gtk_combo_box_set_active( GTK_COMBO_BOX( widget ), 0 ); widget = glade_xml_get_widget( xml, "csspattern" ); gtk_widget_show( widget ); glade_xml_signal_autoconnect( xml ); css_selector_tag_change( widget ); screem_plugin_restore_from_session( plugin, widget ); do { button = gtk_dialog_run( GTK_DIALOG( widget ) ); } while( button == 0 ); screem_plugin_store_in_session( plugin, widget ); if( button == GTK_RESPONSE_APPLY ) { widget = glade_xml_get_widget( xml, "match_box" ); pattern = create_selector( widget ); screem_plugin_insert( plugin, -1, pattern, strlen( pattern ), FALSE ); g_free( pattern ); styles = create_styles( xml ); screem_plugin_insert( plugin, -1, styles, strlen( styles ), FALSE ); g_free( styles ); } widget = glade_xml_get_widget( xml, "csspattern" ); gtk_widget_destroy( widget ); g_object_unref( G_OBJECT( xml ) ); } } void css_selector_next_change( GtkComboBox *omenu ) { GladeXML *xml; GtkWidget *widget; GtkWidget *notebook; gint page; gint pages; gint item; static const gchar *labels[] = { N_( "Rule" ), N_( "Contained in" ), N_( "Child of" ), N_( "After a" ) }; xml = glade_get_widget_tree( GTK_WIDGET( omenu ) ); widget = glade_xml_get_widget( xml, "match_box" ); notebook = g_object_get_data( G_OBJECT( widget ), "notebook" ); page = gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ); item = gtk_combo_box_get_active( omenu ); pages = gtk_notebook_get_n_pages( GTK_NOTEBOOK( notebook ) ); switch( item ) { case ANYWHERE: /* remove all subsequent pages */ while( pages > ( page + 1 ) ) { widget = gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), page + 1 ); xml = glade_get_widget_tree( widget ); gtk_notebook_remove_page( GTK_NOTEBOOK( notebook ), page + 1 ); g_object_unref( xml ); pages --; } break; case SOMEWHERE: case CHILD_OF: case AFTER: if( pages == ( page + 1 ) ) { add_page( notebook, gettext( labels[ item ] ) ); } else { /* change label */ widget = gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), page + 1 ); gtk_notebook_set_tab_label_text( GTK_NOTEBOOK( notebook ), widget, gettext( labels[ item ] ) ); } break; } } void css_selector_tag_change( GtkWidget *widget ) { GladeXML *xml; ScreemPlugin *plugin; const gchar *name; ScreemPage *page; ScreemDTD *dtd; const ScreemDTDElement *elem; const ScreemDTDAttribute *attr; const GSList *attrs; gboolean show; GtkWidget *entry; xml = glade_get_widget_tree( widget ); widget = glade_xml_get_widget( xml, "match_box" ); plugin = SCREEM_PLUGIN( g_object_get_data( G_OBJECT( widget ), "plugin" ) ); widget = glade_xml_get_widget( xml, "tag_menu" ); show = ( ! GTK_WIDGET_IS_SENSITIVE( widget ) ); entry = GTK_BIN( widget )->child; name = gtk_entry_get_text( GTK_ENTRY( entry ) ); page = screem_plugin_get_current_document( plugin ); if( page && ! show ) { dtd = screem_page_get_dtd( page ); elem = screem_dtd_valid_element( dtd, name ); attrs = NULL; if( elem ) { attrs = screem_dtd_element_get_attrs( elem ); } while( attrs ) { attr = (const ScreemDTDAttribute*)attrs->data; name = screem_dtd_attribute_get_name( attr ); if( ! g_strcasecmp( "href", name ) ) { show = TRUE; break; } attrs = attrs->next; } } widget = glade_xml_get_widget( xml, "hyperlink_button" ); gtk_widget_set_sensitive( widget, show ); show &= gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( widget ) ); widget = glade_xml_get_widget( xml, "hyperlink_menu" ); gtk_widget_set_sensitive( widget, show ); } void css_wizard_color_set( GtkWidget *widget, GtkColorButton *cp ) { GdkColor c; gchar *text; gtk_color_button_get_color( cp, &c ); text = screem_gdk_color_to_string( &c, TRUE ); if( GTK_IS_COMBO_BOX_ENTRY( widget ) ) { widget = GTK_BIN( widget )->child; } g_return_if_fail( GTK_IS_ENTRY( widget ) ); gtk_entry_set_text( GTK_ENTRY( widget ), text ); g_free( text ); } /* There should be no need to change any code below here */ enum { ARG_0 }; static void screem_skel_plugin_class_init( ScreemSkelPluginClass *klass ); static void screem_skel_plugin_init( ScreemSkelPlugin *skel_plugin ); static void screem_skel_plugin_finalize( GObject *object ); /* G Object stuff */ #define PARENT_TYPE SCREEM_TYPE_PLUGIN static gpointer parent_class; static void screem_skel_plugin_class_init( ScreemSkelPluginClass *klass ) { GObjectClass *object_class; object_class = G_OBJECT_CLASS( klass ); object_class->finalize = screem_skel_plugin_finalize; parent_class = g_type_class_peek_parent( klass ); } static void screem_skel_plugin_init( ScreemSkelPlugin *skel_plugin ) { skel_plugin->priv = g_new0( ScreemSkelPluginPrivate, 1 ); SCREEM_PLUGIN( skel_plugin )->setup = setup; } static void screem_skel_plugin_finalize( GObject *object ) { ScreemSkelPlugin *skel_plugin; ScreemSkelPluginPrivate *priv; skel_plugin = SCREEM_SKEL_PLUGIN( object ); priv = skel_plugin->priv; cleanup( priv ); g_free( priv ); G_OBJECT_CLASS( parent_class )->finalize( object ); } static GType screem_skel_plugin_get_type() { static GType type = 0; if( ! type ) { static const GTypeInfo info = { sizeof( ScreemSkelPluginClass ), NULL, /* base init */ NULL, /* base finalise */ (GClassInitFunc)screem_skel_plugin_class_init, NULL, /* class finalise */ NULL, /* class data */ sizeof( ScreemSkelPlugin ), 0, /* n_preallocs */ (GInstanceInitFunc)screem_skel_plugin_init }; type = g_type_register_static( PARENT_TYPE, plugin_name, &info, 0 ); } return type; } static ScreemSkelPlugin *screem_skel_plugin_new( void ) { ScreemSkelPlugin *skel_plugin; skel_plugin = SCREEM_SKEL_PLUGIN( g_object_new( SCREEM_TYPE_SKEL_PLUGIN, "name", plugin_name, NULL ) ); return skel_plugin; } G_MODULE_EXPORT void get_details( ScreemPluginDetails **ret ) { ScreemPluginDetails *details; details = g_new0( ScreemPluginDetails, 1 ); details->name = plugin_name; details->displayed_name = displayed_name; details->authors = authors; details->description = description; details->version = version; details->create = screem_skel_plugin_new; details->api_version = SCREEM_PLUGIN_REQUIRED_VERSION; *ret = details; }