Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sundry fixes and enhancements to the keystrokes recorder plugin #980

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
251 changes: 129 additions & 122 deletions keyrecord/src/keyrecord.c
@@ -1,106 +1,88 @@
/*
* keyrecord.c - this file is part of Geany, a fast and lightweight IDE
* keyrecord.c - this file is part of Geany, a fast and lightweight IDE
*
* Copyright 2007-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
* Copyright 2007-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
* Copyright 2007-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
* Copyright 2007-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
*
* 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 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.
* 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.
* 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.
*/



/**
* Keystrokes recorder plugin - plugin for recording sequences of keystrokes and replaying it
*/


#include <geanyplugin.h> /* plugin API, always comes first */
#include "keybindings.h"
#include <geany.h>
#include "Scintilla.h" /* for the SCNotification struct */
#include "stdio.h"
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include "keybindings.h"
#include <assert.h>
//#undef geany
/* text to be shown in the plugin dialog */
static GeanyPlugin* g_plugin = NULL;


static gboolean recording;
static GdkEventKey** recorded_pattern;
static guint recorded_size;
int CAPACITY = 2;
GeanyKeyBinding *record, *play;
static GeanyKeyBinding* record;
static GeanyKeyBinding* play;
static GArray* recorded_key_presses;
static GtkLabel* status_indicator_label;

static GtkStatusbar *geany_statusbar;
static GtkWidget *keyrecord_statusbar_box;
static GtkLabel *keyrecord_state_label;

static void update_status()
static void
update_status(void)
{
if (recording)
gtk_widget_show(keyrecord_state_label);
gtk_widget_show(GTK_WIDGET(status_indicator_label));
else
gtk_widget_hide(keyrecord_state_label);
gtk_widget_hide(GTK_WIDGET(status_indicator_label));
}

static gboolean is_record_key(GdkEventKey *event)

static gboolean
is_record_key(GdkEventKey* event)
{
return event->keyval == record->key
&& (event->state & record->mods) == record->mods;
}

static gboolean is_play_key(GdkEventKey *event)

static gboolean
is_play_key(GdkEventKey* event)
{
return event->keyval == play->key
&& (event->state & play->mods) == play->mods;
}

}


static gboolean
on_key_press(GObject *object, GdkEventKey *event, gpointer user_data)
on_key_press(GObject* object, GdkEventKey* event, gpointer user_data)
{
guint i;
GdkEventKey** tmp = NULL;

if ((recording) && !(is_record_key(event)||is_play_key(event)))
if ((recording) && !(is_record_key(event) || is_play_key(event)))
{
GdkEventKey* new_event = gdk_event_copy(event);
if (recorded_size == CAPACITY)
{
tmp = g_new0(GdkEventKey*, CAPACITY * 2);
for (i = 0; i < recorded_size; i++)
tmp[i] = recorded_pattern[i];
g_free(recorded_pattern);
recorded_pattern = tmp;
CAPACITY *= 2;
}
assert(recorded_size < CAPACITY);
if (recorded_pattern[(recorded_size)] != NULL)
gdk_event_free(recorded_pattern[recorded_size]);
recorded_pattern[recorded_size++] = new_event;
GdkEventKey* new_event = (GdkEventKey*)gdk_event_copy((GdkEvent*)event);
g_array_append_val(recorded_key_presses, new_event);
}

return FALSE;
}


static void
on_document_open(GObject *obj, GeanyDocument *doc, gpointer user_data)
on_document_open(GObject* obj, GeanyDocument* doc, gpointer user_data)
{
GeanyDocument *data;
ScintillaObject *sci;
GeanyDocument* data;
ScintillaObject* sci;

g_return_if_fail(DOC_VALID(doc));
sci = doc->editor->sci;
data = g_new0(GeanyDocument, 1);
Expand All @@ -109,30 +91,29 @@ on_document_open(GObject *obj, GeanyDocument *doc, gpointer user_data)
g_object_set_data_full(G_OBJECT(sci), "keyrecord-userdata", data, g_free);
}

static PluginCallback keyrecord_callbacks[] =

static void
clear_recorded_key_presses()
{
/* 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. */
{ "document-open", (GCallback) &on_document_open, FALSE, NULL },
{ "document-new", (GCallback) &on_document_open, FALSE, NULL },
{ "key-press", (GCallback) &on_key_press, FALSE, NULL },
//{ "editor-notify", (GCallback) &on_editor_notify, FALSE, NULL },
{ NULL, NULL, FALSE, NULL }
};
guint i;

for (i = 0; i < recorded_key_presses->len; i++)
gdk_event_free(g_array_index(recorded_key_presses, GdkEvent*, i));
g_array_set_size(recorded_key_presses, 0);
}


static void
on_record (guint key_id)
{
if (!recording)
{
recording = TRUE;
recorded_size = 0;
clear_recorded_key_presses();
}
else
{
recording = FALSE;
}

update_status();
}

Expand All @@ -142,95 +123,121 @@ on_play (guint key_id)
{
if (!recording)
{
//fprintf(stderr, "play: %d\n", key_id);
guint i;
//// sci_start_undo_action(doc->editor->sci);
for (i = 0; i < (recorded_size); i++)
gtk_main_do_event(recorded_pattern[i]);
guint i;

for (i = 0; i < recorded_key_presses->len; i++)
gtk_main_do_event(g_array_index(recorded_key_presses, GdkEvent*, i));
}
// sci_end_undo_action(doc->editor->sci);
}


static void
status_indicator_init(GeanyPlugin* plugin)
{
GeanyData* geany_data = plugin->geany_data;
GtkStatusbar* geany_statusbar;
GtkHBox* geany_statusbar_box;

geany_statusbar = (GtkStatusbar*)ui_lookup_widget(geany->main_widgets->window, "statusbar");
geany_statusbar_box = (GtkHBox*)gtk_widget_get_parent(GTK_WIDGET(geany_statusbar));
status_indicator_label = (GtkLabel*)gtk_label_new(NULL);
gtk_label_set_markup(status_indicator_label,
"<span foreground='red' weight='bold'>REC</span>");
gtk_box_pack_end(GTK_BOX(geany_statusbar_box),
GTK_WIDGET(status_indicator_label), FALSE, FALSE, 10);
}


static PluginCallback
keyrecord_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. */
{ "document-open", (GCallback) &on_document_open, FALSE, NULL },
{ "document-new", (GCallback) &on_document_open, FALSE, NULL },
{ "key-press", (GCallback) &on_key_press, FALSE, NULL },
{ NULL, NULL, FALSE, NULL }
};


/* Called by Geany to initialize the plugin */
static gboolean keyrecord_init(GeanyPlugin *plugin, gpointer data)
static gboolean
keyrecord_init(GeanyPlugin* plugin, gpointer data)
{
GeanyKeyGroup *group;
GeanyKeyGroup* group;

group = plugin_set_key_group (plugin, "keyrecord", 2, NULL);
record = keybindings_set_item (group, 0, on_record,
0, 0, "record", _("Start/Stop record"), NULL);
play = keybindings_set_item (group, 1, on_play,
0, 0, "play", _("Play"), NULL);
GeanyData* geany_data = plugin->geany_data;
recorded_pattern = g_new0(GdkEventKey*, CAPACITY);
0, 0, "record", _("Start/Stop record"), NULL);
play = keybindings_set_item (group, 1, on_play,
0, 0, "play", _("Play"), NULL);

GeanyData* geany_data = plugin->geany_data;
recorded_key_presses = g_array_new(FALSE, FALSE, sizeof(GdkEventKey*));

guint i = 0;
foreach_document(i) {
foreach_document(i)
on_document_open(NULL, documents[i], NULL);
}

recording = FALSE;
recorded_size = 0;

geany_plugin_set_data(plugin, plugin, NULL);

g_plugin = plugin;

geany_statusbar = gtk_widget_get_parent(geany->main_widgets->progressbar);
keyrecord_statusbar_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
keyrecord_state_label = gtk_label_new(NULL);
gtk_label_set_markup(keyrecord_state_label,
"<span foreground='red' weight='bold'>REC</span>");
gtk_widget_show(keyrecord_statusbar_box);
gtk_container_add(keyrecord_statusbar_box, keyrecord_state_label);
gtk_box_pack_end(geany_statusbar, keyrecord_statusbar_box, FALSE, FALSE, 0);

status_indicator_init(plugin);
update_status();

geany_plugin_set_data(plugin, plugin, NULL);

return TRUE;
}


/* Called by Geany before unloading the plugin.
* Here any UI changes should be removed, memory freed and any other finalization done.
* Here any UI changes should be removed,
* memory freed and any other finalization done.
* Be sure to leave Geany as it was before demo_init(). */
static void keyrecord_cleanup(GeanyPlugin *plugin, gpointer _data)
static void
keyrecord_cleanup(GeanyPlugin* plugin, gpointer _data)
{
GeanyData* geany_data = plugin->geany_data;
guint i;
for (i = 0; i < CAPACITY; i++)
if (recorded_pattern[i] != NULL)
gdk_event_free(recorded_pattern[i]);
g_free(recorded_pattern);

foreach_document(i)
guint i;

g_array_free(recorded_key_presses, TRUE);

foreach_document(i)
{
gpointer data;
ScintillaObject *sci;
ScintillaObject* sci;

sci = documents[i]->editor->sci;
data = g_object_steal_data(G_OBJECT(sci), "keyrecord-userdata");
g_free(data);
}

gtk_widget_destroy(keyrecord_state_label);
gtk_widget_destroy(keyrecord_statusbar_box);
gtk_widget_destroy(GTK_WIDGET(status_indicator_label));
}

void geany_load_module(GeanyPlugin *plugin)

void
geany_load_module(GeanyPlugin* plugin)
{
/* main_locale_init() must be called for your package before any localization can be done */
/* main_locale_init(), must be called for your package
* before any localization can be done */
main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);

plugin->info->name = _("Keystrokes recorder");
plugin->info->description = _("Allows to record some sequence of keystrokes and replay it");
plugin->info->description =
_("Allows user to record some sequence of keystrokes and replay it");
plugin->info->version = "0.12";
plugin->info->author = _("tunyash");
plugin->info->author = _("tunyash");

plugin->funcs->init = keyrecord_init;
plugin->funcs->configure = NULL;
plugin->funcs->help = NULL; /* This demo has no help but it is an option */
plugin->funcs->help = NULL;
plugin->funcs->cleanup = keyrecord_cleanup;
plugin->funcs->callbacks = keyrecord_callbacks;

// API 237, Geany 1.34, PR #1829, added "key-press" signal.
/* API 237, Geany 1.34, PR #1829, added "key-press" signal */
GEANY_PLUGIN_REGISTER(plugin, 237);
}