[Glade-devel] Glade 3 development

Damon Chaplin damon@karuna.uklinux.net
22 Apr 2004 12:55:07 +0100


--=-BBVfnbkYCfN5D9bhxLvU
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

On Sun, 2004-04-18 at 22:56, Joaquin Cuenca Abela wrote:

> >  o Support for the event mask property.
> 
> These 3 bugs should be independent of the changes that I have in my hard
> drive, so if you can start by these hopefully we will not step on each other
> feet

Here's a patch to support flags properties.
They seem to work in the GUI and to save and load OK.

Damon


--=-BBVfnbkYCfN5D9bhxLvU
Content-Disposition: attachment; filename=glade3.patch2
Content-Type: text/x-patch; name=glade3.patch2; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Index: glade-editor.c
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-editor.c,v
retrieving revision 1.52
diff -u -r1.52 glade-editor.c
--- glade-editor.c	21 Apr 2004 21:03:40 -0000	1.52
+++ glade-editor.c	22 Apr 2004 11:49:59 -0000
@@ -481,6 +481,162 @@
 	glade_editor_property_changed_unichar (entry, property);
 }
 
+#define FLAGS_COLUMN_SETTING		0
+#define FLAGS_COLUMN_SYMBOL		1
+
+static void
+flag_toggled (GtkCellRendererToggle *cell,
+	      gchar                 *path_string,
+	      GtkTreeModel          *model)
+{
+	GtkTreeIter iter;
+	gboolean setting;
+
+	gtk_tree_model_get_iter_from_string (model, &iter, path_string);
+
+	gtk_tree_model_get (model, &iter,
+			    FLAGS_COLUMN_SETTING, &setting,
+			    -1);
+
+	setting = setting ? FALSE : TRUE;
+
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+			    FLAGS_COLUMN_SETTING, setting,
+			    -1);
+}
+
+
+static void
+glade_editor_property_show_flags_dialog (GtkWidget *entry,
+					 GladeEditorProperty *property)
+{
+	GtkWidget *editor;
+	GtkWidget *dialog;
+	GtkWidget *scrolled_window;
+	GtkListStore *model;
+	GtkWidget *tree_view;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+	GFlagsClass *class;
+	gint flag_num, response_id;
+	guint value;
+
+	g_return_if_fail (property != NULL);
+
+	editor = gtk_widget_get_toplevel (entry);
+	dialog = gtk_dialog_new_with_buttons (_("Set Flags"),
+					      GTK_WINDOW (editor),
+					      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+					      GTK_STOCK_CANCEL,
+					      GTK_RESPONSE_CANCEL,
+					      GTK_STOCK_OK,
+					      GTK_RESPONSE_OK,
+					      NULL);
+	gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400);
+
+	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	gtk_widget_show (scrolled_window);
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+			    scrolled_window, TRUE, TRUE, 0);
+
+	/* Create the treeview using a simple list model with 3 columns. */
+	model = gtk_list_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING,
+				    G_TYPE_STRING);
+
+	tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
+	gtk_widget_show (tree_view);
+	gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+
+	column = gtk_tree_view_column_new ();
+
+	renderer = gtk_cell_renderer_toggle_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+					     "active", FLAGS_COLUMN_SETTING,
+					     NULL);
+	g_signal_connect (renderer, "toggled",
+			  G_CALLBACK (flag_toggled), model);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+					     "text", FLAGS_COLUMN_SYMBOL,
+					     NULL);
+
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+
+	/* Populate the model with the flags. */
+	class = g_type_class_ref (G_VALUE_TYPE (property->property->value));
+	value = g_value_get_flags (property->property->value);
+
+	/* Step through each of the flags in the class. */
+	for (flag_num = 0; flag_num < class->n_values; flag_num++) {
+		GtkTreeIter iter;
+		guint mask;
+		gboolean setting;
+
+		mask = class->values[flag_num].value;
+		setting = ((value & mask) == mask) ? TRUE : FALSE;
+
+		/* Add a row to represent the flag. */
+		gtk_list_store_append (model, &iter);
+		gtk_list_store_set (model, &iter,
+				    FLAGS_COLUMN_SETTING,
+				    setting,
+				    FLAGS_COLUMN_SYMBOL,
+				    class->values[flag_num].value_name,
+				    -1);
+	}
+
+	/* Run the dialog. */
+	response_id = gtk_dialog_run (GTK_DIALOG (dialog));
+
+	/* If the user selects OK, update the flags property. */
+	if (response_id == GTK_RESPONSE_OK) {
+		GtkTreeIter iter;
+		guint new_value = 0;
+
+		gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
+
+		/* Step through each of the flags in the class, checking if
+		   the corresponding toggle in the dialog is selected, If it
+		   is, OR the flags' mask with the new value. */
+		for (flag_num = 0; flag_num < class->n_values; flag_num++) {
+			gboolean setting;
+
+			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
+					    FLAGS_COLUMN_SETTING, &setting,
+					    -1);
+
+			if (setting)
+				new_value |= class->values[flag_num].value;
+
+			gtk_tree_model_iter_next (GTK_TREE_MODEL (model),
+						  &iter);
+		}
+
+		/* If the new_value is different from the old value, we need
+		   to update the property. */
+		if (new_value != value) {
+			GValue val = { 0, };
+
+			g_value_init (&val, G_VALUE_TYPE (property->property->value));
+			g_value_set_flags (&val, new_value);
+			glade_command_set_property (property->property, &val);
+		}
+
+	}
+
+	g_type_class_unref (class);
+
+	gtk_widget_destroy (dialog);
+}
+
 /* ================================ Create inputs ==================================== */
 static GtkWidget *
 glade_editor_create_input_enum_item (GladeEditorProperty *property,
@@ -530,7 +686,26 @@
 static GtkWidget *
 glade_editor_create_input_flags (GladeEditorProperty *property) 
 {
-	return gtk_label_new ("Fix Me");
+	GtkWidget *hbox;
+	GtkWidget *entry;
+	GtkWidget *button;
+
+	hbox = gtk_hbox_new (FALSE, 0);
+
+	entry = gtk_entry_new ();
+	gtk_entry_set_editable (GTK_ENTRY (entry), FALSE);
+	gtk_widget_show (entry);
+	gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+
+	button = gtk_button_new_with_label ("...");
+	gtk_widget_show (button);
+	gtk_box_pack_start (GTK_BOX (hbox), button,  FALSE, FALSE, 0);
+
+	g_signal_connect (G_OBJECT (button), "clicked",
+			  G_CALLBACK (glade_editor_property_show_flags_dialog),
+			  property);
+
+	return hbox;
 }
 
 static GtkWidget *
@@ -1175,7 +1350,30 @@
 static void
 glade_editor_property_load_flags (GladeEditorProperty *property)
 {
-	glade_implement_me ();
+	GtkBoxChild *child;
+	GtkWidget *entry;
+	GValue tmp_value = { 0, };
+	gchar *text;
+
+	g_return_if_fail (property != NULL);
+	g_return_if_fail (property->property != NULL);
+	g_return_if_fail (property->property->value != NULL);
+	g_return_if_fail (property->input != NULL);
+	g_return_if_fail (GTK_IS_HBOX (property->input));
+
+	/* The entry should be the first child. */
+	child = GTK_BOX (property->input)->children->data;
+	entry = child->widget;
+	g_return_if_fail (GTK_IS_ENTRY (entry));
+
+	/* Transform the GValue from flags to a string. */
+	g_value_init (&tmp_value, G_TYPE_STRING);
+	g_value_transform (property->property->value, &tmp_value);
+	text = g_strescape (g_value_get_string (&tmp_value), NULL);
+	g_value_unset (&tmp_value);
+
+	gtk_entry_set_text (GTK_ENTRY (entry), text);
+	g_free (text);
 }
 
 static void
Index: glade-property-class.h
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-property-class.h,v
retrieving revision 1.27
diff -u -r1.27 glade-property-class.h
--- glade-property-class.h	7 Nov 2003 16:20:17 -0000	1.27
+++ glade-property-class.h	22 Apr 2004 11:50:22 -0000
@@ -132,8 +132,9 @@
 			    * and is NULL for other poperties.
 			    * [See glade-choice.h]
 			    */
-	GType enum_type;   /* If it is GLADE_PROPERTY_TYPE_ENUM, this holds
-			    * the GType of the enum, otherwise it's 0.
+	GType enum_type;   /* If it is GLADE_PROPERTY_TYPE_ENUM or
+			    * GLADE_PROPERTY_TYPE_FLAGS, this holds
+			    * the GType of the enum or flags, otherwise it's 0.
 			    */
 
 	gboolean optional; /* Some properties are optional by nature like
Index: glade-property-class.c
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-property-class.c,v
retrieving revision 1.53
diff -u -r1.53 glade-property-class.c
--- glade-property-class.c	27 Jan 2004 18:27:40 -0000	1.53
+++ glade-property-class.c	22 Apr 2004 11:50:52 -0000
@@ -259,11 +259,7 @@
 	} else if (G_IS_PARAM_SPEC_ENUM (spec)) {
 		return GLADE_PROPERTY_TYPE_ENUM;
 	} else if (G_IS_PARAM_SPEC_FLAGS (spec)) {
-		/* FIXME: We should implement the "events" property */
-		if (g_ascii_strcasecmp (spec->name, "events") == 0)
-			return GLADE_PROPERTY_TYPE_ERROR;
-		else
-			return GLADE_PROPERTY_TYPE_FLAGS;
+		return GLADE_PROPERTY_TYPE_FLAGS;
 	} else if (G_IS_PARAM_SPEC_DOUBLE (spec)) {
 		return GLADE_PROPERTY_TYPE_DOUBLE;
 	} else if (G_IS_PARAM_SPEC_LONG (spec)) {
@@ -385,6 +381,17 @@
 			}
 		}
 		break;
+	case GLADE_PROPERTY_TYPE_FLAGS:
+		{
+			GValue tmp_value = { 0, };
+
+			g_value_init (&tmp_value, G_TYPE_STRING);
+			g_value_transform (value, &tmp_value);
+			string = g_strescape (g_value_get_string (&tmp_value),
+					      NULL);
+			g_value_unset (&tmp_value);
+		}
+		break;
 	default:
 		g_warning ("Could not make string from gvalue for type %s\n",
 			 glade_property_type_enum_to_string (type));
@@ -393,6 +400,86 @@
 	return string;
 }
 
+/* This is copied exactly from libglade. I've just renamed the function. */
+static guint
+glade_property_class_make_flags_from_string (GType type, const char *string)
+{
+    GFlagsClass *fclass;
+    gchar *endptr, *prevptr;
+    guint i, j, ret = 0;
+    char *flagstr;
+
+    ret = strtoul(string, &endptr, 0);
+    if (endptr != string) /* parsed a number */
+	return ret;
+
+    fclass = g_type_class_ref(type);
+
+
+    flagstr = g_strdup (string);
+    for (ret = i = j = 0; ; i++) {
+	gboolean eos;
+
+	eos = flagstr [i] == '\0';
+	
+	if (eos || flagstr [i] == '|') {
+	    GFlagsValue *fv;
+	    const char  *flag;
+	    gunichar ch;
+
+	    flag = &flagstr [j];
+            endptr = &flagstr [i];
+
+	    if (!eos) {
+		flagstr [i++] = '\0';
+		j = i;
+	    }
+
+            /* trim spaces */
+	    for (;;)
+	      {
+		ch = g_utf8_get_char (flag);
+		if (!g_unichar_isspace (ch))
+		  break;
+		flag = g_utf8_next_char (flag);
+	      }
+
+	    while (endptr > flag)
+	      {
+		prevptr = g_utf8_prev_char (endptr);
+		ch = g_utf8_get_char (prevptr);
+		if (!g_unichar_isspace (ch))
+		  break;
+		endptr = prevptr;
+	      }
+
+	    if (endptr > flag)
+	      {
+		*endptr = '\0';
+		fv = g_flags_get_value_by_name (fclass, flag);
+
+		if (!fv)
+		  fv = g_flags_get_value_by_nick (fclass, flag);
+
+		if (fv)
+		  ret |= fv->value;
+		else
+		  g_warning ("Unknown flag: '%s'", flag);
+	      }
+
+	    if (eos)
+		break;
+	}
+    }
+    
+    g_free (flagstr);
+
+    g_type_class_unref(fclass);
+
+    return ret;
+}
+
+
 GValue *
 glade_property_class_make_gvalue_from_string (GladePropertyClass *property_class,
 					      const gchar *string)
@@ -454,6 +541,14 @@
 			}
 		}
 		break;
+	case GLADE_PROPERTY_TYPE_FLAGS:
+		{
+			guint flags;
+
+			g_value_init (value, property_class->enum_type);
+			flags = glade_property_class_make_flags_from_string (property_class->enum_type, string);
+			g_value_set_flags (value, flags);
+		}
 	case GLADE_PROPERTY_TYPE_OBJECT:
 		break;
 	default:
@@ -562,6 +657,7 @@
 		property_class->enum_type = spec->value_type;
 		break;
 	case GLADE_PROPERTY_TYPE_FLAGS:
+		property_class->enum_type = spec->value_type;
 		break;
 	case GLADE_PROPERTY_TYPE_STRING:
 		break;

--=-BBVfnbkYCfN5D9bhxLvU--