SF.net SVN: geany: [2621] trunk

ntrel at users.sourceforge.net ntrel at xxxxx
Wed May 28 13:05:13 UTC 2008


Revision: 2621
          http://geany.svn.sourceforge.net/geany/?rev=2621&view=rev
Author:   ntrel
Date:     2008-05-28 06:05:09 -0700 (Wed, 28 May 2008)

Log Message:
-----------
Note: this breaks the plugin API.
Remove plugin symbol configure().
Add plugin symbol plugin_configure() which is used to tell Geany a
widget to pack into the plugin preferences dialog, and connect a
response callback for when the dialog receives a user decision.
This allows Geany to in future implement a common preferences dialog
for all plugins, without breaking the plugin API/ABI.
Add Apply button for plugin preference dialogs (to indicate plugins
should handle the apply response as well as OK, as a multiple plugin
configuration dialog would want an apply button).

Modified Paths:
--------------
    trunk/ChangeLog
    trunk/doc/plugin-symbols.c
    trunk/plugins/autosave.c
    trunk/plugins/demoplugin.c
    trunk/plugins/filebrowser.c
    trunk/src/plugindata.h
    trunk/src/plugins.c

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2008-05-27 17:03:43 UTC (rev 2620)
+++ trunk/ChangeLog	2008-05-28 13:05:09 UTC (rev 2621)
@@ -1,3 +1,19 @@
+2008-05-28  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
+
+ * src/plugindata.h, src/plugins.c, doc/plugin-symbols.c,
+   plugins/demoplugin.c, plugins/filebrowser.c, plugins/autosave.c:
+   Note: this breaks the plugin API.
+   Remove plugin symbol configure().
+   Add plugin symbol plugin_configure() which is used to tell Geany a
+   widget to pack into the plugin preferences dialog, and connect a
+   response callback for when the dialog receives a user decision.
+   This allows Geany to in future implement a common preferences dialog
+   for all plugins, without breaking the plugin API/ABI.
+   Add Apply button for plugin preference dialogs (to indicate plugins
+   should handle the apply response as well as OK, as a multiple plugin
+   configuration dialog would want an apply button).
+
+
 2008-05-27  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
 
  * src/plugins.c:

Modified: trunk/doc/plugin-symbols.c
===================================================================
--- trunk/doc/plugin-symbols.c	2008-05-27 17:03:43 UTC (rev 2620)
+++ trunk/doc/plugin-symbols.c	2008-05-28 13:05:09 UTC (rev 2621)
@@ -76,10 +76,13 @@
 KeyBindingGroup plugin_key_group[1];
 
 
-/** Called when the plugin should show a configure dialog to let the user set some basic
- * plugin configuration. Optionally, can be omitted when not needed.
- * @param parent The Plugin Manager dialog widget. */
-void configure(GtkWidget *parent);
+/** Called before showing the plugin preferences dialog to let the user set some basic
+ * plugin configuration options. Can be omitted when not needed.
+ * @param dialog The plugin preferences dialog widget - this should only be used to
+ * connect the @c "response" signal. If settings should be read from the dialog, the
+ * reponse will be either @c GTK_RESPONSE_OK or @c GTK_RESPONSE_APPLY.
+ * @return A container widget holding preference widgets. */
+GtkWidget* plugin_configure(GtkDialog *dialog);
 
 /** Called after loading the plugin.
  * @param data The same as #geany_data. */

Modified: trunk/plugins/autosave.c
===================================================================
--- trunk/plugins/autosave.c	2008-05-27 17:03:43 UTC (rev 2620)
+++ trunk/plugins/autosave.c	2008-05-28 13:05:09 UTC (rev 2621)
@@ -116,23 +116,67 @@
 }
 
 
-void configure(GtkWidget *parent)
+static struct
 {
-	GtkWidget *dialog, *label, *spin, *vbox, *hbox, *checkbox, *radio1, *radio2;
+	GtkWidget *interval_spin;
+	GtkWidget *print_msg_checkbox;
+	GtkWidget *save_all_radio;
+}
+pref_widgets;
 
-	dialog = gtk_dialog_new_with_buttons(_("Auto Save"),
-		GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT,
-		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
-	vbox = p_ui->dialog_vbox_new(GTK_DIALOG(dialog));
-	gtk_widget_set_name(dialog, "GeanyDialog");
-	gtk_box_set_spacing(GTK_BOX(vbox), 6);
+static void
+on_configure_response(GtkDialog *dialog, gint response, G_GNUC_UNUSED gpointer user_data)
+{
+	if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY)
+	{
+		GKeyFile *config = g_key_file_new();
+		gchar *data;
+		gchar *config_dir = g_path_get_dirname(config_file);
 
+		interval = gtk_spin_button_get_value_as_int((GTK_SPIN_BUTTON(pref_widgets.interval_spin)));
+		print_msg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.print_msg_checkbox));
+		save_all = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.save_all_radio));
+
+		g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
+
+		g_key_file_set_integer(config, "autosave", "interval", interval);
+		g_key_file_set_boolean(config, "autosave", "print_messages", print_msg);
+		g_key_file_set_boolean(config, "autosave", "save_all", save_all);
+
+		if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) && p_utils->mkdir(config_dir, TRUE) != 0)
+		{
+			p_dialogs->show_msgbox(GTK_MESSAGE_ERROR,
+				_("Plugin configuration directory could not be created."));
+		}
+		else
+		{
+			/* write config to file */
+			data = g_key_file_to_data(config, NULL, NULL);
+			p_utils->write_file(config_file, data);
+			g_free(data);
+		}
+
+		set_timeout(); /* apply the changes */
+
+		g_free(config_dir);
+		g_key_file_free(config);
+	}
+}
+
+
+GtkWidget *plugin_configure(GtkDialog *dialog)
+{
+	GtkWidget *vbox, *label, *spin, *hbox, *checkbox, *radio1, *radio2;
+
+	vbox = gtk_vbox_new(FALSE, 6);
+
 	label = gtk_label_new(_("Auto save interval:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_container_add(GTK_CONTAINER(vbox), label);
 
 	spin = gtk_spin_button_new_with_range(1, 1800, 1);
 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), interval);
+	pref_widgets.interval_spin = spin;
 
 	label = gtk_label_new(_("seconds"));
 
@@ -147,6 +191,7 @@
 	gtk_button_set_focus_on_click(GTK_BUTTON(checkbox), FALSE);
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), print_msg);
 	gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 5);
+	pref_widgets.print_msg_checkbox = checkbox;
 
 	radio1 = gtk_radio_button_new_with_label(NULL,
 		_("Save only current open file"));
@@ -158,45 +203,12 @@
 	gtk_button_set_focus_on_click(GTK_BUTTON(radio2), FALSE);
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio2), save_all);
 	gtk_container_add(GTK_CONTAINER(vbox), radio2);
+	pref_widgets.save_all_radio = radio2;
 
 	gtk_widget_show_all(vbox);
 
-	/* run the dialog and check for the response code */
-	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
-	{
-		GKeyFile *config = g_key_file_new();
-		gchar *data;
-		gchar *config_dir = g_path_get_dirname(config_file);
-
-		interval = gtk_spin_button_get_value_as_int((GTK_SPIN_BUTTON(spin)));
-		print_msg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox));
-		save_all = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio2));
-
-		g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
-
-		g_key_file_set_integer(config, "autosave", "interval", interval);
-		g_key_file_set_boolean(config, "autosave", "print_messages", print_msg);
-		g_key_file_set_boolean(config, "autosave", "save_all", save_all);
-
-		if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) && p_utils->mkdir(config_dir, TRUE) != 0)
-		{
-			p_dialogs->show_msgbox(GTK_MESSAGE_ERROR,
-				_("Plugin configuration directory could not be created."));
-		}
-		else
-		{
-			/* write config to file */
-			data = g_key_file_to_data(config, NULL, NULL);
-			p_utils->write_file(config_file, data);
-			g_free(data);
-		}
-
-		set_timeout(); /* apply the changes */
-
-		g_free(config_dir);
-		g_key_file_free(config);
-	}
-	gtk_widget_destroy(dialog);
+	g_signal_connect(dialog, "response", G_CALLBACK(on_configure_response), NULL);
+	return vbox;
 }
 
 

Modified: trunk/plugins/demoplugin.c
===================================================================
--- trunk/plugins/demoplugin.c	2008-05-27 17:03:43 UTC (rev 2620)
+++ trunk/plugins/demoplugin.c	2008-05-28 13:05:09 UTC (rev 2621)
@@ -101,48 +101,54 @@
 }
 
 
+/* Callback connected in plugin_configure(). */
+static void
+on_configure_response(GtkDialog *dialog, gint response, gpointer user_data)
+{
+	/* catch OK or Apply clicked */
+	if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY)
+	{
+		/* We only have one pref here, but for more you would use a struct for user_data */
+		GtkWidget *entry = GTK_WIDGET(user_data);
+
+		g_free(welcome_text);
+		welcome_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+		/* maybe the plugin should write here the settings into a file
+		 * (e.g. using GLib's GKeyFile API)
+		 * all plugin specific files should be created in:
+		 * app->configdir G_DIR_SEPARATOR_S plugins G_DIR_SEPARATOR_S pluginname G_DIR_SEPARATOR_S
+		 * e.g. this could be: ~/.geany/plugins/Demo/, please use app->configdir */
+	}
+}
+
+
 /* Called by Geany to show the plugin's configure dialog. This function is always called after
  * plugin_init() was called.
  * You can omit this function if the plugin doesn't need to be configured.
  * Note: parent is the parent window which can be used as the transient window for the created
  *       dialog. */
-void configure(GtkWidget *parent)
+GtkWidget *plugin_configure(GtkDialog *dialog)
 {
-	GtkWidget *dialog, *label, *entry, *vbox;
+	GtkWidget *label, *entry, *vbox;
 
 	/* example configuration dialog */
-	dialog = gtk_dialog_new_with_buttons(_("Demo"),
-		GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT,
-		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
-	vbox = p_ui->dialog_vbox_new(GTK_DIALOG(dialog));
-	gtk_widget_set_name(dialog, "GeanyDialog");
-	gtk_box_set_spacing(GTK_BOX(vbox), 6);
+	vbox = gtk_vbox_new(FALSE, 6);
 
 	/* add a label and a text entry to the dialog */
 	label = gtk_label_new(_("Welcome text to show:"));
-	gtk_widget_show(label);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	entry = gtk_entry_new();
-	gtk_widget_show(entry);
 	if (welcome_text != NULL)
 		gtk_entry_set_text(GTK_ENTRY(entry), welcome_text);
 
 	gtk_container_add(GTK_CONTAINER(vbox), label);
 	gtk_container_add(GTK_CONTAINER(vbox), entry);
-	gtk_widget_show(vbox);
 
-	/* run the dialog and check for the response code */
-	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
-	{
-		g_free(welcome_text);
-		welcome_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
-		/* maybe the plugin should write here the settings into a file
-		 * (e.g. using GLib's GKeyFile API)
-		 * all plugin specific files should be created in:
-		 * app->configdir G_DIR_SEPARATOR_S plugins G_DIR_SEPARATOR_S pluginname G_DIR_SEPARATOR_S
-		 * e.g. this could be: ~/.geany/plugins/Demo/, please use app->configdir */
-	}
-	gtk_widget_destroy(dialog);
+	gtk_widget_show_all(vbox);
+
+	/* Connect a callback for when the user clicks a dialog button */
+	g_signal_connect(dialog, "response", G_CALLBACK(on_configure_response), entry);
+	return vbox;
 }
 
 

Modified: trunk/plugins/filebrowser.c
===================================================================
--- trunk/plugins/filebrowser.c	2008-05-27 17:03:43 UTC (rev 2620)
+++ trunk/plugins/filebrowser.c	2008-05-28 13:05:09 UTC (rev 2621)
@@ -952,17 +952,62 @@
 }
 
 
-void configure(GtkWidget *parent)
+static struct
 {
-	GtkWidget *dialog, *label, *entry, *checkbox_of, *checkbox_hf, *vbox;
+	GtkWidget *open_cmd_entry;
+	GtkWidget *show_hidden_checkbox;
+	GtkWidget *hide_objects_checkbox;
+}
+pref_widgets;
+
+static void
+on_configure_response(GtkDialog *dialog, gint response, gpointer user_data)
+{
+	if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY)
+	{
+		GKeyFile *config = g_key_file_new();
+		gchar *data;
+		gchar *config_dir = g_path_get_dirname(config_file);
+
+		g_free(open_cmd);
+		open_cmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(pref_widgets.open_cmd_entry)));
+		show_hidden_files = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.show_hidden_checkbox));
+		hide_object_files = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.hide_objects_checkbox));
+
+		g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
+
+		g_key_file_set_string(config, "filebrowser", "open_command", open_cmd);
+		g_key_file_set_boolean(config, "filebrowser", "show_hidden_files", show_hidden_files);
+		g_key_file_set_boolean(config, "filebrowser", "hide_object_files", hide_object_files);
+
+		if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) && p_utils->mkdir(config_dir, TRUE) != 0)
+		{
+			p_dialogs->show_msgbox(GTK_MESSAGE_ERROR,
+				_("Plugin configuration directory could not be created."));
+		}
+		else
+		{
+			/* write config to file */
+			data = g_key_file_to_data(config, NULL, NULL);
+			p_utils->write_file(config_file, data);
+			g_free(data);
+		}
+
+		/* apply the changes */
+		refresh();
+
+		g_free(config_dir);
+		g_key_file_free(config);
+	}
+}
+
+
+GtkWidget *plugin_configure(GtkDialog *dialog)
+{
+	GtkWidget *label, *entry, *checkbox_of, *checkbox_hf, *vbox;
 	GtkTooltips *tooltips = gtk_tooltips_new();
 
-	dialog = gtk_dialog_new_with_buttons(_("File Browser"),
-		GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT,
-		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
-	vbox = p_ui->dialog_vbox_new(GTK_DIALOG(dialog));
-	gtk_widget_set_name(dialog, "GeanyDialog");
-	gtk_box_set_spacing(GTK_BOX(vbox), 6);
+	vbox = gtk_vbox_new(FALSE, 6);
 
 	label = gtk_label_new(_("External open command:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
@@ -978,11 +1023,13 @@
 		  "%d will be replaced with the path name of the selected file without the filename"),
 		  NULL);
 	gtk_container_add(GTK_CONTAINER(vbox), entry);
+	pref_widgets.open_cmd_entry = entry;
 
 	checkbox_hf = gtk_check_button_new_with_label(_("Show hidden files"));
 	gtk_button_set_focus_on_click(GTK_BUTTON(checkbox_hf), FALSE);
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox_hf), show_hidden_files);
 	gtk_box_pack_start(GTK_BOX(vbox), checkbox_hf, FALSE, FALSE, 5);
+	pref_widgets.show_hidden_checkbox = checkbox_hf;
 
 	checkbox_of = gtk_check_button_new_with_label(_("Hide object files"));
 	gtk_button_set_focus_on_click(GTK_BUTTON(checkbox_of), FALSE);
@@ -992,48 +1039,12 @@
 		  "*.o, *.obj. *.so, *.dll, *.a, *.lib"),
 		  NULL);
 	gtk_box_pack_start(GTK_BOX(vbox), checkbox_of, FALSE, FALSE, 5);
+	pref_widgets.hide_objects_checkbox = checkbox_of;
 
-
 	gtk_widget_show_all(vbox);
 
-	/* run the dialog and check for the response code */
-	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
-	{
-		GKeyFile *config = g_key_file_new();
-		gchar *data;
-		gchar *config_dir = g_path_get_dirname(config_file);
-
-		g_free(open_cmd);
-		open_cmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
-		show_hidden_files = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox_hf));
-		hide_object_files = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox_of));
-
-		g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
-
-		g_key_file_set_string(config, "filebrowser", "open_command", open_cmd);
-		g_key_file_set_boolean(config, "filebrowser", "show_hidden_files", show_hidden_files);
-		g_key_file_set_boolean(config, "filebrowser", "hide_object_files", hide_object_files);
-
-		if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) && p_utils->mkdir(config_dir, TRUE) != 0)
-		{
-			p_dialogs->show_msgbox(GTK_MESSAGE_ERROR,
-				_("Plugin configuration directory could not be created."));
-		}
-		else
-		{
-			/* write config to file */
-			data = g_key_file_to_data(config, NULL, NULL);
-			p_utils->write_file(config_file, data);
-			g_free(data);
-		}
-
-		/* apply the changes */
-		refresh();
-
-		g_free(config_dir);
-		g_key_file_free(config);
-	}
-	gtk_widget_destroy(dialog);
+	g_signal_connect(dialog, "response", G_CALLBACK(on_configure_response), NULL);
+	return vbox;
 }
 
 

Modified: trunk/src/plugindata.h
===================================================================
--- trunk/src/plugindata.h	2008-05-27 17:03:43 UTC (rev 2620)
+++ trunk/src/plugindata.h	2008-05-28 13:05:09 UTC (rev 2621)
@@ -36,7 +36,7 @@
 
 /* The API version should be incremented whenever any plugin data types below are
  * modified or appended to. */
-static const gint api_version = 64;
+static const gint api_version = 65;
 
 /* The ABI version should be incremented whenever existing fields in the plugin
  * data types below have to be changed or reordered. It should stay the same if fields

Modified: trunk/src/plugins.c
===================================================================
--- trunk/src/plugins.c	2008-05-27 17:03:43 UTC (rev 2620)
+++ trunk/src/plugins.c	2008-05-28 13:05:09 UTC (rev 2621)
@@ -74,9 +74,9 @@
 	gsize		signal_ids_len;
 	KeyBindingGroup	*key_group;
 
-	void	(*init) (GeanyData *data);	/* Called when the plugin is enabled */
-	void	(*configure) (GtkWidget *parent);	/* plugin configure dialog, optionally */
-	void	(*cleanup) (void);			/* Called when the plugin is disabled or when Geany exits */
+	void		(*init) (GeanyData *data);			/* Called when the plugin is enabled */
+	GtkWidget*	(*configure) (GtkDialog *dialog);	/* plugin configure dialog, optional */
+	void		(*cleanup) (void);					/* Called when the plugin is disabled or when Geany exits */
 }
 Plugin;
 
@@ -467,9 +467,9 @@
 	plugin->init(&geany_data);
 
 	/* store some function pointers for later use */
-	g_module_symbol(plugin->module, "configure", (void *) &plugin->configure);
+	g_module_symbol(plugin->module, "plugin_configure", (void *) &plugin->configure);
 	g_module_symbol(plugin->module, "plugin_cleanup", (void *) &plugin->cleanup);
-	if (plugin->init != NULL && plugin->cleanup == NULL)
+	if (plugin->cleanup == NULL)
 	{
 		if (app->debug_mode)
 			g_warning("Plugin '%s' has no plugin_cleanup() function - there may be memory leaks!",
@@ -898,6 +898,7 @@
 	GtkWidget *description_label;
 	GtkWidget *configure_button;
 } PluginManagerWidgets;
+
 static PluginManagerWidgets pm_widgets;
 
 
@@ -1036,6 +1037,39 @@
 }
 
 
+static void configure_plugin(Plugin *p)
+{
+	GtkWidget *parent = pm_widgets.dialog;
+	GtkWidget *prefs_page, *dialog, *vbox;
+
+	dialog = gtk_dialog_new_with_buttons(p->info.name,
+		GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+	gtk_widget_set_name(dialog, "GeanyDialog");
+
+	vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
+	gtk_widget_show(vbox);
+
+	prefs_page = p->configure(GTK_DIALOG(dialog));
+
+	if (! GTK_IS_WIDGET(prefs_page))
+	{
+		geany_debug("Invalid widget returned from plugin_configure() in plugin \"%s\"!",
+			p->info.name);
+	}
+	else
+	{
+		gtk_container_add(GTK_CONTAINER(vbox), prefs_page);
+
+		/* run the dialog */
+		while (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_APPLY);
+	}
+	gtk_widget_destroy(dialog);
+}
+
+
 void pm_on_configure_button_clicked(GtkButton *button, gpointer user_data)
 {
 	GtkTreeModel *model;
@@ -1050,7 +1084,7 @@
 
 		if (p != NULL)
 		{
-			p->configure(pm_widgets.dialog);
+			configure_plugin(p);
 		}
 	}
 }


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list