Revision: 3391 http://geany.svn.sourceforge.net/geany/?rev=3391&view=rev Author: eht16 Date: 2008-12-17 16:00:18 +0000 (Wed, 17 Dec 2008)
Log Message: ----------- Add "editor-notify" to the plugin API. This signal is emitted whenever something in an editor widget changes, e.g. a character was typed.
Modified Paths: -------------- trunk/ChangeLog trunk/doc/Doxyfile.in trunk/doc/plugins.dox trunk/plugins/demoplugin.c trunk/src/document.c trunk/src/editor.c trunk/src/editor.h trunk/src/geany.h trunk/src/geanyobject.c trunk/src/geanyobject.h trunk/src/plugindata.h
Modified: trunk/ChangeLog =================================================================== --- trunk/ChangeLog 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/ChangeLog 2008-12-17 16:00:18 UTC (rev 3391) @@ -6,6 +6,12 @@ Retitle the Terminal/VTE preferences tab section. Change (again) the key combinations for creating rectangular selections. + * doc/Doxyfile.in, doc/plugins.dox, plugins/demoplugin.c, src/editor.c, + src/editor.h, src/document.c, src/geanyobject.c, src/geanyobject.h, + src/geany.h, src/plugindata.h: + Add "editor-notify" to the plugin API. + This signal is emitted whenever something in an editor widget + changes, e.g. a character was typed.
2008-12-16 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
Modified: trunk/doc/Doxyfile.in =================================================================== --- trunk/doc/Doxyfile.in 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/doc/Doxyfile.in 2008-12-17 16:00:18 UTC (rev 3391) @@ -40,7 +40,7 @@ "endsignaldef= " \ "signalproto=@code " \ "endsignalproto=@endcode " \ - "signaldesc=@par Description: " \ + "signaldesc=" \ "signals=@b Signals: " \ "endsignals= " OPTIMIZE_OUTPUT_FOR_C = YES
Modified: trunk/doc/plugins.dox =================================================================== --- trunk/doc/plugins.dox 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/doc/plugins.dox 2008-12-17 16:00:18 UTC (rev 3391) @@ -56,6 +56,7 @@ * * To use plugin signals in Geany, you simply create a PluginCallback array, list the signals * you want to listen to and create the appropiate signal callbacks for each signal. + * The callback array is read @a after plugin_init() has been called. * @note The PluginCallback array has to be ended with a final NULL entry. * * The following code demonstrates how to use signals in Geany plugins. The code can be inserted @@ -185,8 +186,46 @@ * @param user_data user data. * @endsignaldef * + * @signaldef editor-notify + * @signalproto + * gboolean user_function(GObject *obj, GeanyEditor *editor, SCNotification *nt, + * gpointer user_data); + * @endsignalproto + * @signaldesc + * This signal is sent whenever something in the editor widget changes (character added, + * fold level changes, clicks to the line number margin, ...). + * A detailed description of possible notifications and the SCNotification can be found at + * http://www.scintilla.org/ScintillaDoc.html#Notifications. * + * If you connect to this signal, you must check @c nt->nmhdr.code for the notification type + * to prevent handling unwanted notifications. This is important because for instance SCN_UPDATEUI + * is sent very often whereas you probably don't want to handle this notification. * + * By default, the signal is sent before Geany's default handler is processing the event. + * Your callback function should return FALSE to allow Geany processing the event as well. If you + * want to prevent this for some reason, return TRUE. + * Please use this with care as it can break basic functionality of Geany. + * + * The signal can be sent after Geany's default handler has been run when you set + * PluginCallback::after field to TRUE. + * + * An example callback implemention of this signal can be found in the Demo plugin. + * + * @warning This signal has much power and should be used carefully. You should especially + * care about the return value; make sure to return TRUE only if it is necessary + * and in the correct situations. + * + * @param obj a GeanyObject instance, should be ignored. + * @param editor The current GeanyEditor. + * @param nt A pointer to the SCNotification struct which holds additional information for + * the event. + * @param user_data user data. + * @return @c TRUE to stop other handlers from being invoked for the event. + * @c FALSE to propagate the event further. + * @endsignaldef + * + * + * * @page howto Plugin Howto * * @section intro Introduction
Modified: trunk/plugins/demoplugin.c =================================================================== --- trunk/plugins/demoplugin.c 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/plugins/demoplugin.c 2008-12-17 16:00:18 UTC (rev 3391) @@ -38,11 +38,13 @@ #include "geany.h" /* for the GeanyApp data type */ #include "support.h" /* for the _() translation macro (see also po/POTFILES.in) */ #include "ui_utils.h" +#include "Scintilla.h" /* for the SCNotification struct */
#include "plugindata.h" /* this defines the plugin API */ #include "geanyfunctions.h" /* this wraps geany_functions function pointers */
+ /* These items are set by Geany before plugin_init() is called. */ GeanyPlugin *geany_plugin; GeanyData *geany_data; @@ -62,6 +64,61 @@ static gchar *welcome_text = NULL;
+ +static gboolean on_editor_notify(GObject *object, GeanyEditor *editor, + SCNotification *nt, gpointer data) +{ + /* For detailed documentation about the SCNotification struct, please see + * http://www.scintilla.org/ScintillaDoc.html#Notifications. */ + switch (nt->nmhdr.code) + { + case SCN_UPDATEUI: + /* This notification is sent very often, you should not do time-consuming tasks here */ + break; + case SCN_CHARADDED: + /* For demonstrating purposes simply print the typed character in the status bar */ + ui_set_statusbar(FALSE, _("Typed character: %c"), nt->ch); + break; + case SCN_URIDROPPED: + { + /* Show a message dialog with the dropped URI list when files (i.e. a list of + * filenames) is dropped to the editor widget) */ + if (nt->text != NULL) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new( + GTK_WINDOW(geany->main_widgets->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + _("The following files were dropped:")); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), + "%s", nt->text); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } + /* we return TRUE here which prevents Geany from processing the SCN_URIDROPPED + * notification, i.e. Geany won't open the passed files */ + return TRUE; + } + } + + return FALSE; +} + + +PluginCallback plugin_callbacks[] = +{ + /* Set 'after' (third field) to TRUE to run the callback @a after the default handler. + * If 'after' is FALSE, the callback is run @a before the default handler, so the plugin + * can prevent Geany from processing the notification. Use this with care. */ + { "editor-notify", (GCallback) &on_editor_notify, FALSE, NULL }, + { NULL, NULL, FALSE, NULL } +}; + + /* Callback when the menu item is clicked. */ static void item_activate(GtkMenuItem *menuitem, gpointer gdata)
Modified: trunk/src/document.c =================================================================== --- trunk/src/document.c 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/src/document.c 2008-12-17 16:00:18 UTC (rev 3391) @@ -595,7 +595,7 @@ sci_goto_pos(doc->editor->sci, 0, TRUE);
/* "the" SCI signal (connect after initial setup(i.e. adding text)) */ - g_signal_connect(doc->editor->sci, "sci-notify", G_CALLBACK(on_editor_notification), doc); + g_signal_connect(doc->editor->sci, "sci-notify", G_CALLBACK(editor_sci_notify_cb), doc);
g_signal_emit_by_name(geany_object, "document-new", doc);
@@ -1118,7 +1118,7 @@ doc->real_path = get_real_path_from_utf8(doc->file_name);
/* "the" SCI signal (connect after initial setup(i.e. adding text)) */ - g_signal_connect(doc->editor->sci, "sci-notify", G_CALLBACK(on_editor_notification), doc); + g_signal_connect(doc->editor->sci, "sci-notify", G_CALLBACK(editor_sci_notify_cb), doc);
use_ft = (ft != NULL) ? ft : filetypes_detect_from_document(doc); }
Modified: trunk/src/editor.c =================================================================== --- trunk/src/editor.c 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/src/editor.c 2008-12-17 16:00:18 UTC (rev 3391) @@ -249,8 +249,6 @@ }
-typedef struct SCNotification SCNotification; - static void fold_symbol_click(ScintillaObject *sci, SCNotification *nt) { gint line = sci_get_line_from_position(sci, nt->position); @@ -624,18 +622,29 @@ }
-/* callback func called by all editors when a signal arises */ -void on_editor_notification(GtkWidget *widget, gint scn, gpointer lscn, gpointer user_data) +/* Callback for the "sci-notify" signal to emit a "editor-notify" signal. + * Plugins can connect to the "editor-notify" signal. */ +void editor_sci_notify_cb(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gint scn, + gpointer scnt, gpointer data) { - SCNotification *nt; + GeanyDocument *doc = data; + gboolean retval; + + g_return_if_fail(doc != NULL); + + g_signal_emit_by_name(geany_object, "editor-notify", doc->editor, scnt, &retval); +} + + +static gboolean on_editor_notify(G_GNUC_UNUSED GeanyObject *object, GeanyEditor *editor, + SCNotification *nt, gpointer data) +{ ScintillaObject *sci; - GeanyDocument *doc = user_data; - GeanyEditor *editor; + GeanyDocument *doc = editor->document;
editor = doc->editor; sci = editor->sci;
- nt = lscn; switch (nt->nmhdr.code) { case SCN_SAVEPOINTLEFT: @@ -754,6 +763,8 @@ break; } } + /* we always return FALSE here to let plugins handle the event to */ + return FALSE; }
@@ -4158,6 +4169,10 @@ memset(&editor_prefs, 0, sizeof(GeanyEditorPrefs)); memset(&indent_prefs, 0, sizeof(GeanyIndentPrefs)); editor_prefs.indentation = &indent_prefs; + + /* use g_signal_connect_after() to allow plugins connecting to the signal before the default + * handler (on_editor_notify) is called */ + g_signal_connect_after(geany_object, "editor-notify", G_CALLBACK(on_editor_notify), NULL); }
Modified: trunk/src/editor.h =================================================================== --- trunk/src/editor.h 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/src/editor.h 2008-12-17 16:00:18 UTC (rev 3391) @@ -131,7 +131,7 @@
/** Editor-owned fields for each document. */ -typedef struct GeanyEditor +struct GeanyEditor { GeanyDocument *document; /**< The document associated with the editor. */ ScintillaObject *sci; /**< The Scintilla editor @c GtkWidget. */ @@ -141,8 +141,7 @@ gfloat scroll_percent; GeanyIndentType indent_type; /* Use editor_get_indent_prefs() instead. */ gboolean line_breaking; /**< Whether to split long lines as you type. */ -} -GeanyEditor; +};
typedef struct @@ -153,9 +152,9 @@
extern EditorInfo editor_info;
+typedef struct SCNotification SCNotification;
- void editor_init(void);
GeanyEditor *editor_create(GeanyDocument *doc); @@ -164,7 +163,7 @@
ScintillaObject *editor_create_widget(GeanyEditor *editor);
-void on_editor_notification(GtkWidget* editor, gint scn, gpointer lscn, gpointer user_data); +void editor_sci_notify_cb(GtkWidget *widget, gint scn, gpointer scnt, gpointer data);
gboolean editor_start_auto_complete(GeanyEditor *editor, gint pos, gboolean force);
Modified: trunk/src/geany.h =================================================================== --- trunk/src/geany.h 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/src/geany.h 2008-12-17 16:00:18 UTC (rev 3391) @@ -56,6 +56,7 @@
/* Common forward declarations */ typedef struct GeanyDocument GeanyDocument; +typedef struct GeanyEditor GeanyEditor; typedef struct GeanyFiletype GeanyFiletype;
Modified: trunk/src/geanyobject.c =================================================================== --- trunk/src/geanyobject.c 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/src/geanyobject.c 2008-12-17 16:00:18 UTC (rev 3391) @@ -81,10 +81,10 @@ static void geany_cclosure_marshal_VOID__STRING_INT_POINTER(GClosure *closure, GValue *ret_val, guint n_param_vals, const GValue *param_values, gpointer hint, gpointer mdata) { - typedef gboolean (*GMarshalFunc_VOID__STRING_INT_POINTER) + typedef gboolean (*GeanyMarshalFunc_VOID__STRING_INT_POINTER) (gpointer data1, gconstpointer arg_1, gint arg_2, gpointer arg_3, gpointer data2);
- register GMarshalFunc_VOID__STRING_INT_POINTER callback; + register GeanyMarshalFunc_VOID__STRING_INT_POINTER callback; register GCClosure* cc = (GCClosure*) closure; register gpointer data1, data2;
@@ -100,7 +100,7 @@ data1 = g_value_peek_pointer(param_values + 0); data2 = closure->data; } - callback = (GMarshalFunc_VOID__STRING_INT_POINTER) (mdata ? mdata : cc->callback); + callback = (GeanyMarshalFunc_VOID__STRING_INT_POINTER) (mdata ? mdata : cc->callback); callback(data1, g_value_get_string(param_values + 1), g_value_get_int(param_values + 2), @@ -109,8 +109,59 @@ }
+static gboolean boolean_handled_accumulator(GSignalInvocationHint *ihint, GValue *return_accu, + const GValue *handler_return, gpointer dummy) +{ + gboolean continue_emission, signal_handled; + + signal_handled = g_value_get_boolean(handler_return); + g_value_set_boolean(return_accu, signal_handled); + continue_emission = !signal_handled; + + return continue_emission; +} + + +static void geany_cclosure_marshal_BOOL__POINTER_POINTER( GClosure *closure, GValue *return_value, + guint n_param_values, const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) +{ + typedef gboolean (*GeanyMarshalFunc_BOOLEAN__POINTER_POINTER) + (gpointer data1, gpointer arg_1, gpointer arg_2, gpointer data2); + + register GeanyMarshalFunc_BOOLEAN__POINTER_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail(return_value != NULL); + g_return_if_fail(n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA(closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer(param_values + 0); + } + else + { + data1 = g_value_peek_pointer(param_values + 0); + data2 = closure->data; + } + callback = (GeanyMarshalFunc_BOOLEAN__POINTER_POINTER) + (marshal_data ? marshal_data : cc->callback); + + v_return = callback(data1, + g_value_get_pointer(param_values + 1), + g_value_get_pointer(param_values + 2), + data2); + + g_value_set_boolean(return_value, v_return); +} + + static void create_signals(GObjectClass *g_object_class) { + /* Document signals */ geany_object_signals[GCB_DOCUMENT_NEW] = g_signal_new ( "document-new", G_OBJECT_CLASS_TYPE (g_object_class), @@ -157,6 +208,7 @@ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ /* Project signals */ geany_object_signals[GCB_PROJECT_OPEN] = g_signal_new ( "project-open", G_OBJECT_CLASS_TYPE (g_object_class), @@ -184,6 +236,7 @@ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ /* Editor signals */ geany_object_signals[GCB_UPDATE_EDITOR_MENU] = g_signal_new ( "update-editor-menu", G_OBJECT_CLASS_TYPE (g_object_class), @@ -193,6 +246,15 @@ geany_cclosure_marshal_VOID__STRING_INT_POINTER, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER); + geany_object_signals[GCB_EDITOR_NOTIFY] = g_signal_new ( + "editor-notify", + G_OBJECT_CLASS_TYPE (g_object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeanyObjectClass, update_editor_menu), + boolean_handled_accumulator, NULL, + geany_cclosure_marshal_BOOL__POINTER_POINTER, + G_TYPE_BOOLEAN, 2, + G_TYPE_POINTER, G_TYPE_POINTER); }
Modified: trunk/src/geanyobject.h =================================================================== --- trunk/src/geanyobject.h 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/src/geanyobject.h 2008-12-17 16:00:18 UTC (rev 3391) @@ -43,6 +43,7 @@ GCB_PROJECT_SAVE, GCB_PROJECT_CLOSE, GCB_UPDATE_EDITOR_MENU, + GCB_EDITOR_NOTIFY, GCB_MAX } GeanyCallbackId;
@@ -81,6 +82,7 @@ void (*project_save)(GKeyFile *keyfile); void (*project_close)(void); void (*update_editor_menu)(const gchar *word, gint click_pos, GeanyDocument *doc); + gboolean (*editor_notify)(GeanyEditor *editor, gpointer scnt); };
GType geany_object_get_type (void);
Modified: trunk/src/plugindata.h =================================================================== --- trunk/src/plugindata.h 2008-12-17 15:59:25 UTC (rev 3390) +++ trunk/src/plugindata.h 2008-12-17 16:00:18 UTC (rev 3391) @@ -45,7 +45,7 @@ enum { /** The Application Programming Interface (API) version, incremented * whenever any plugin data types are modified or appended to. */ - GEANY_API_VERSION = 118, + GEANY_API_VERSION = 119,
/** The Application Binary Interface (ABI) version, incremented whenever * existing fields in the plugin data types have to be changed or reordered. */
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.