4
* $Id: glame_accelerator.c,v 1.15.2.2 2002/01/16 21:08:02 richi Exp $
6
* Copyright (C) 2001 Richard Guenther
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
#include <sys/types.h>
36
#include <xmlmemory.h>
44
#include "glame_accelerator.h"
47
/* Note that accelerator handling is strictly single-threaded,
48
* so no locking is required and state passing can be done via
52
struct accel_cb_data {
57
GLAME_LIST_HEAD(_glame_accel_list);
58
static int stringhash(const char *spec);
59
HASH(accel, struct accel, 8,
60
(strcmp(accel->spec, spec) == 0
61
&& accel->state == (state & accel->state_mask)),
63
(stringhash(accel->spec)),
64
const char *spec, guint state)
67
static SCM gls_glame_accel_edit_dialog_new(SCM s_scope, SCM s_edit);
74
static int stringhash(const char *spec)
82
static gint accel_cb(GtkWidget *widget, GdkEventKey *event,
83
struct accel_cb_data *data)
88
snprintf(spec, 255, "%s/%s",
90
gdk_keyval_name(event->keyval));
91
if (!(accel = hash_find_accel(spec, event->state)))
94
glame_gh_safe_eval_str(accel->action);
99
static void accel_cb_cleanup(GtkObject *object, struct accel_cb_data *data)
110
int glame_accel_init()
114
glame_add_accels_from_file(PKGDATADIR "/default-accels");
115
glame_add_accels_from_file("./gui/default-accels");
116
snprintf(fname, 255, "%s/.glame-accels", getenv("HOME"));
117
glame_add_accels_from_file(fname);
119
gh_new_procedure2_0("glame-accel-edit-dialog",
120
gls_glame_accel_edit_dialog_new);
125
void glame_accel_sync()
133
snprintf(fname, 255, "%s/.glame-accels", getenv("HOME"));
134
if (!(f = fopen(fname, "w")))
137
doc = glame_accels_to_xml();
138
xmlDocDumpMemory(doc, &xml, &size);
139
fwrite(xml, size, 1, f);
145
static int add_accels(const char *scope, xmlNodePtr node)
147
#ifndef xmlChildrenNode
150
node = node->xmlChildrenNode;
156
if (strcmp(node->name, "scope") == 0) {
157
char combined_scope[256];
160
node_scope = xmlGetProp(node, "scope");
163
snprintf(combined_scope, 255,
164
"%s%s/", scope, node_scope);
165
add_accels(combined_scope, node);
167
} else if (strcmp(node->name, "accel") == 0) {
168
char *spec, *action, *s;
169
guint state, state_mask;
173
s = xmlGetProp(node, "state");
175
if (sscanf(s, "%i", &state) != 1)
177
state_mask = GDK_SHIFT_MASK|GDK_CONTROL_MASK
179
s = xmlGetProp(node, "mask");
181
if (sscanf(s, "%i", &state_mask) != 1)
183
spec = xmlGetProp(node, "spec");
186
action = xmlNodeGetContent(node);
187
if (!action || strlen(action) == 0)
190
snprintf(full_spec, 255, "%s%s", scope, spec);
191
glame_accel_add(full_spec, state_mask, state, action);
194
/* with libxml2 we need to handle extra
196
if (strcmp(node->name, "text") != 0)
205
int glame_add_accels_from_xml(const xmlDocPtr xml)
208
return add_accels(scope, xmlDocGetRootElement(xml));
211
int glame_add_accels_from_file(const char *filename)
219
if ((fd = open(filename, O_RDONLY)) == -1)
223
if (!(f = fdopen(fd, "r"))) {
227
xml = malloc(st.st_size+1);
228
fread(xml, st.st_size, 1, f);
229
xml[st.st_size] = '\0';
230
if ((doc = xmlParseMemory(xml, st.st_size))) {
231
res = glame_add_accels_from_xml(doc);
241
static xmlNodePtr getScopeNode(xmlNodePtr root, const char *scope,
244
char accel_scope[256];
245
char combined_scope[256];
246
char *node_scope, *p;
249
strncpy(accel_scope, accel->spec, 255);
250
if ((p = strrchr(accel_scope, '/')))
252
if (strcmp(scope, accel_scope) == 0)
255
#ifndef xmlChildrenNode
258
node = root->xmlChildrenNode;
261
if (strcmp(node->name, "scope") == 0) {
262
node_scope = xmlGetProp(node, "scope");
265
snprintf(combined_scope, 255,
266
"%s%s/", scope, node_scope);
267
if (strncmp(combined_scope, accel_scope,
268
strlen(combined_scope)) == 0)
277
/* Need to create the scope node. */
278
node = xmlNewChild(root, NULL, "scope", NULL);
279
node_scope = &accel_scope[strlen(scope)];
280
*strchr(node_scope, '/') = '\0';
281
xmlSetProp(node, "scope", node_scope);
282
snprintf(combined_scope, 255, "%s%s/", scope,
286
return getScopeNode(node, combined_scope, accel);
289
xmlDocPtr glame_accels_to_xml()
292
xmlNodePtr docroot, scope, entry;
293
struct accel *accel, *dummy;
296
doc = xmlNewDoc("1.0");
297
docroot = xmlNewNode(NULL, "glame-accels");
298
glame_accel_safe_foreach(dummy, accel) {
299
scope = getScopeNode(docroot, "", accel);
300
entry = xmlNewChild(scope, NULL, "accel", NULL);
301
snprintf(s, 255, "%i", accel->state);
302
xmlSetProp(entry, "state", s);
303
xmlSetProp(entry, "spec", strrchr(accel->spec, '/')+1);
304
xmlNodeSetContent(entry, accel->action);
306
xmlDocSetRootElement(doc, docroot);
312
static void _free_accel(struct accel *accel)
314
hash_remove_accel(accel);
315
glame_list_del(&accel->list);
321
int glame_accel_add(const char *spec, guint state_mask, guint state,
324
struct accel *accel, *old;
326
if (!spec || !action)
329
if (!(accel = ALLOC(struct accel)))
331
hash_init_accel(accel);
332
GLAME_INIT_LIST_HEAD(&accel->list);
333
accel->state_mask = state_mask;
334
accel->state = state;
335
accel->spec = strdup(spec);
336
accel->action = strdup(action);
338
if ((old = hash_find_accel(spec, state)))
341
glame_list_add(&accel->list, &_glame_accel_list);
342
hash_add_accel(accel);
347
void glame_accel_del(const char *spec, guint state)
351
if (!(accel = hash_find_accel(spec, state)))
356
void glame_accel_del_all(const char *scope)
358
struct accel *accel, *dummy;
362
glame_list_safe_foreach(&_glame_accel_list, struct accel, list, dummy, accel) {
363
if (strncmp(accel->spec, scope, len) == 0)
369
guint glame_accel_install(GtkWidget *widget,
370
const char *scope, ...)
372
struct accel_cb_data *data;
376
if (!widget || !scope)
380
if (va_arg(va, const char *) != NULL) {
382
return 0; /* FIXME: later... */
385
if (!(data = ALLOC(struct accel_cb_data)))
387
data->widget = widget;
388
data->scope = strdup(scope);
390
handler = gtk_signal_connect(GTK_OBJECT(widget), "key_press_event",
391
(GtkSignalFunc)accel_cb, data);
392
gtk_signal_connect(GTK_OBJECT(widget), "destroy",
393
(GtkSignalFunc)accel_cb_cleanup, data);
399
guint glame_accel_widget_data_cb(GtkWidget *widget, gpointer spec)
403
if (!(accel = hash_find_accel(spec, 0)))
406
glame_gh_safe_eval_str(accel->action);
412
GtkWidget *glame_accel_edit_widget(const char *scope, int edit)
414
GtkWidget *sw, *clist;
415
static char *labels[] = { "Scope", "Key", "Binding" };
416
struct accel *accel, *dummy;
418
clist = gtk_clist_new_with_titles(3, labels);
419
gtk_clist_set_column_auto_resize(GTK_CLIST(clist), 0, TRUE);
420
gtk_clist_set_column_auto_resize(GTK_CLIST(clist), 1, TRUE);
421
gtk_clist_set_column_auto_resize(GTK_CLIST(clist), 2, TRUE);
423
glame_accel_safe_foreach(dummy, accel) {
426
char *line[3], *p, *pp;
428
if (strncmp(accel->spec, scope, strlen(scope)) != 0)
432
strncpy(e_scope, accel->spec + strlen(scope), 1024);
433
if ((p = strrchr(e_scope, '/'))) {
442
if (accel->state & GDK_SHIFT_MASK)
443
p += sprintf(p, "SHIFT-");
444
if (accel->state & GDK_CONTROL_MASK)
445
p += sprintf(p, "CTRL-");
446
if (accel->state & GDK_MOD1_MASK)
447
p += sprintf(p, "MOD1-");
448
if (accel->state & GDK_MOD2_MASK)
449
p += sprintf(p, "MOD2-");
450
if (accel->state & GDK_MOD3_MASK)
451
p += sprintf(p, "MOD3-");
452
if (accel->state & GDK_MOD4_MASK)
453
p += sprintf(p, "MOD4-");
454
if (accel->state & GDK_MOD5_MASK)
455
p += sprintf(p, "MOD5-");
456
if (!(pp = strrchr(accel->spec, '/')))
462
sprintf(p, "%s", pp+1);
466
line[2] = accel->action;
468
gtk_clist_append(GTK_CLIST(clist), line);
471
sw = gtk_scrolled_window_new(NULL, NULL);
472
gtk_container_add(GTK_CONTAINER(sw), clist);
473
gtk_widget_set_usize(sw, 500, 300);
478
GtkWidget *glame_accel_edit_dialog(const char *scope, int edit,
481
GtkWidget *dialog, *accel_widget;
483
accel_widget = glame_accel_edit_widget(scope, edit);
487
dialog = gtk_type_new(gnome_dialog_get_type());
488
gnome_dialog_append_button_with_pixmap(
489
GNOME_DIALOG(dialog), _("Close"), GNOME_STOCK_PIXMAP_CLOSE);
490
gnome_dialog_set_sensitive(GNOME_DIALOG(dialog), 0, TRUE);
492
gnome_dialog_set_parent(GNOME_DIALOG(dialog), parent);
494
gtk_container_add(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox), accel_widget);
495
gtk_widget_show_all(accel_widget);
500
static SCM gls_glame_accel_edit_dialog_new(SCM s_scope, SCM s_edit)
504
SCM_ASSERT(gh_string_p(s_scope), s_scope, SCM_ARG1,
505
"glame-accel-edit-dialog-new");
506
SCM_ASSERT(gh_boolean_p(s_edit), s_edit, SCM_ARG2,
507
"glame-accel-edit-dialog-new");
509
scope = gh_scm2newstr(s_scope, &len);
510
gnome_dialog_run_and_close(GNOME_DIALOG(
511
glame_accel_edit_dialog(scope, gh_scm2bool(s_edit), NULL)));
514
return SCM_UNSPECIFIED;