Add "editor-notify" to the plugin API.

This signal is emitted whenever something in an editor widget changes, e.g. a character was typed.

git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@3391 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
Enrico Tröger 2008-12-17 16:00:18 +00:00
parent 5ceb85d214
commit 44b7d27b50
11 changed files with 201 additions and 20 deletions

View File

@ -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>

View File

@ -40,7 +40,7 @@ ALIASES = "signal=- @ref " \
"endsignaldef= " \
"signalproto=@code " \
"endsignalproto=@endcode " \
"signaldesc=@par Description: " \
"signaldesc=" \
"signals=@b Signals: " \
"endsignals= "
OPTIMIZE_OUTPUT_FOR_C = YES

View File

@ -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,6 +186,44 @@ PluginCallback plugin_callbacks[] =
* @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

View File

@ -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 GtkWidget *main_menu_item = NULL;
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)

View File

@ -595,7 +595,7 @@ GeanyDocument *document_new_file(const gchar *utf8_filename, GeanyFiletype *ft,
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 @@ GeanyDocument *document_open_file_full(GeanyDocument *doc, const gchar *filename
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);
}

View File

@ -249,8 +249,6 @@ static gboolean is_style_php(gint style)
}
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 @@ static void auto_update_margin_width(GeanyDocument *doc)
}
/* 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)
{
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)
{
SCNotification *nt;
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 @@ void on_editor_notification(GtkWidget *widget, gint scn, gpointer lscn, gpointer
break;
}
}
/* we always return FALSE here to let plugins handle the event to */
return FALSE;
}
@ -4158,6 +4169,10 @@ void editor_init(void)
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);
}

View File

@ -131,7 +131,7 @@ extern GeanyEditorPrefs editor_prefs;
/** 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 @@ typedef struct GeanyEditor
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,7 +152,7 @@ typedef struct
extern EditorInfo editor_info;
typedef struct SCNotification SCNotification;
void editor_init(void);
@ -164,7 +163,7 @@ void editor_destroy(GeanyEditor *editor);
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);

View File

@ -56,6 +56,7 @@
/* Common forward declarations */
typedef struct GeanyDocument GeanyDocument;
typedef struct GeanyEditor GeanyEditor;
typedef struct GeanyFiletype GeanyFiletype;

View File

@ -81,10 +81,10 @@ GType geany_object_get_type(void)
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 @@ static void geany_cclosure_marshal_VOID__STRING_INT_POINTER(GClosure *closure, G
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 void geany_cclosure_marshal_VOID__STRING_INT_POINTER(GClosure *closure, G
}
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 @@ static void create_signals(GObjectClass *g_object_class)
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 @@ static void create_signals(GObjectClass *g_object_class)
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 @@ static void create_signals(GObjectClass *g_object_class)
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);
}

View File

@ -43,6 +43,7 @@ typedef enum
GCB_PROJECT_SAVE,
GCB_PROJECT_CLOSE,
GCB_UPDATE_EDITOR_MENU,
GCB_EDITOR_NOTIFY,
GCB_MAX
} GeanyCallbackId;
@ -81,6 +82,7 @@ struct _GeanyObjectClass
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);

View File

@ -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. */