~ubuntu-branches/ubuntu/utopic/glame/utopic

« back to all changes in this revision

Viewing changes to src/gui/glame_accelerator.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Kobras
  • Date: 2002-04-09 17:14:12 UTC
  • Revision ID: james.westby@ubuntu.com-20020409171412-jzpnov7mbz2w6zsr
Tags: upstream-0.6.2
ImportĀ upstreamĀ versionĀ 0.6.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * glame_accelerator.c
 
3
 *
 
4
 * $Id: glame_accelerator.c,v 1.15.2.2 2002/01/16 21:08:02 richi Exp $
 
5
 * 
 
6
 * Copyright (C) 2001 Richard Guenther
 
7
 *
 
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.
 
12
 *
 
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.
 
17
 *
 
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
 
21
 *
 
22
 */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#include <config.h>
 
26
#endif
 
27
 
 
28
#include <sys/types.h>
 
29
#include <sys/stat.h>
 
30
#include <fcntl.h>
 
31
#include <unistd.h>
 
32
#include <stdlib.h>
 
33
#include <stdio.h>
 
34
#include <ctype.h>
 
35
#include <string.h>
 
36
#include <xmlmemory.h>
 
37
#include <parser.h>
 
38
#include <gdk/gdk.h>
 
39
#include <gnome.h>
 
40
#include "util.h"
 
41
#include "list.h"
 
42
#include "hash.h"
 
43
#include "glscript.h"
 
44
#include "glame_accelerator.h"
 
45
 
 
46
 
 
47
/* Note that accelerator handling is strictly single-threaded,
 
48
 * so no locking is required and state passing can be done via
 
49
 * global variables.
 
50
 */ 
 
51
 
 
52
struct accel_cb_data {
 
53
        GtkWidget *widget;
 
54
        char *scope;
 
55
};
 
56
 
 
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)),
 
62
     (stringhash(spec)),
 
63
     (stringhash(accel->spec)),
 
64
     const char *spec, guint state)
 
65
 
 
66
 
 
67
static SCM gls_glame_accel_edit_dialog_new(SCM s_scope, SCM s_edit);
 
68
 
 
69
 
 
70
/*
 
71
 * Helpers.
 
72
 */
 
73
 
 
74
static int stringhash(const char *spec)
 
75
{
 
76
        int val = 0;
 
77
        while (*spec)
 
78
                val += *(spec++);
 
79
        return val;
 
80
}
 
81
 
 
82
static gint accel_cb(GtkWidget *widget, GdkEventKey *event,
 
83
                     struct accel_cb_data *data)
 
84
{
 
85
        struct accel *accel;
 
86
        char spec[256];
 
87
 
 
88
        snprintf(spec, 255, "%s/%s",
 
89
                 data->scope,
 
90
                 gdk_keyval_name(event->keyval));
 
91
        if (!(accel = hash_find_accel(spec, event->state)))
 
92
                return FALSE;
 
93
 
 
94
        glame_gh_safe_eval_str(accel->action);
 
95
 
 
96
        return TRUE;
 
97
}
 
98
 
 
99
static void accel_cb_cleanup(GtkObject *object, struct accel_cb_data *data)
 
100
{
 
101
        free(data->scope);
 
102
        free(data);
 
103
}
 
104
 
 
105
 
 
106
/*
 
107
 * API.
 
108
 */
 
109
 
 
110
int glame_accel_init()
 
111
{
 
112
        char fname[256];
 
113
 
 
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);
 
118
 
 
119
        gh_new_procedure2_0("glame-accel-edit-dialog",
 
120
                            gls_glame_accel_edit_dialog_new);
 
121
 
 
122
        return 0;
 
123
}
 
124
 
 
125
void glame_accel_sync()
 
126
{
 
127
        xmlDocPtr doc;
 
128
        xmlChar *xml;
 
129
        int size;
 
130
        char fname[256];
 
131
        FILE *f;
 
132
 
 
133
        snprintf(fname, 255, "%s/.glame-accels", getenv("HOME"));
 
134
        if (!(f = fopen(fname, "w")))
 
135
                return;
 
136
 
 
137
        doc = glame_accels_to_xml();
 
138
        xmlDocDumpMemory(doc, &xml, &size);
 
139
        fwrite(xml, size, 1, f);
 
140
        fclose(f);
 
141
        free(xml);
 
142
        xmlFreeDoc(doc);
 
143
}
 
144
 
 
145
static int add_accels(const char *scope, xmlNodePtr node)
 
146
{
 
147
#ifndef xmlChildrenNode
 
148
        node = node->childs;
 
149
#else
 
150
        node = node->xmlChildrenNode;
 
151
#endif
 
152
        if (!node)
 
153
                return 0;
 
154
 
 
155
        while (node) {
 
156
                if (strcmp(node->name, "scope") == 0) {
 
157
                        char combined_scope[256];
 
158
                        char *node_scope;
 
159
 
 
160
                        node_scope = xmlGetProp(node, "scope");
 
161
                        if (!node_scope)
 
162
                                return -1;
 
163
                        snprintf(combined_scope, 255,
 
164
                                 "%s%s/", scope, node_scope);
 
165
                        add_accels(combined_scope, node);
 
166
 
 
167
                } else if (strcmp(node->name, "accel") == 0) {
 
168
                        char *spec, *action, *s;
 
169
                        guint state, state_mask;
 
170
                        char full_spec[256];
 
171
 
 
172
                        state = 0;
 
173
                        s = xmlGetProp(node, "state");
 
174
                        if (s)
 
175
                                if (sscanf(s, "%i", &state) != 1)
 
176
                                        return -1;
 
177
                        state_mask = GDK_SHIFT_MASK|GDK_CONTROL_MASK
 
178
                                |GDK_MOD1_MASK;
 
179
                        s = xmlGetProp(node, "mask");
 
180
                        if (s)
 
181
                                if (sscanf(s, "%i", &state_mask) != 1)
 
182
                                        return -1;
 
183
                        spec = xmlGetProp(node, "spec");
 
184
                        if (!spec)
 
185
                                return -1;
 
186
                        action = xmlNodeGetContent(node);
 
187
                        if (!action || strlen(action) == 0)
 
188
                                return -1;
 
189
 
 
190
                        snprintf(full_spec, 255, "%s%s", scope, spec);
 
191
                        glame_accel_add(full_spec, state_mask, state, action);
 
192
 
 
193
                } else {
 
194
                        /* with libxml2 we need to handle extra
 
195
                         * whitespace. */
 
196
                        if (strcmp(node->name, "text") != 0)
 
197
                                return -1;
 
198
                }
 
199
 
 
200
                node = node->next;
 
201
        }
 
202
 
 
203
        return 0;
 
204
}
 
205
int glame_add_accels_from_xml(const xmlDocPtr xml)
 
206
{
 
207
        char scope[8] = "";
 
208
        return add_accels(scope, xmlDocGetRootElement(xml));
 
209
}
 
210
 
 
211
int glame_add_accels_from_file(const char *filename)
 
212
{
 
213
        int fd, res;
 
214
        FILE *f;
 
215
        struct stat st;
 
216
        char *xml;
 
217
        xmlDocPtr doc;
 
218
 
 
219
        if ((fd = open(filename, O_RDONLY)) == -1)
 
220
                return -1;
 
221
        fstat(fd, &st);
 
222
 
 
223
        if (!(f = fdopen(fd, "r"))) {
 
224
                close(fd);
 
225
                return -1;
 
226
        }
 
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);
 
232
                xmlFreeDoc(doc);
 
233
        } else
 
234
                res = -1;
 
235
        free(xml);
 
236
        fclose(f);
 
237
 
 
238
        return res;
 
239
}
 
240
 
 
241
static xmlNodePtr getScopeNode(xmlNodePtr root, const char *scope,
 
242
                               struct accel *accel)
 
243
{
 
244
        char accel_scope[256];
 
245
        char combined_scope[256];
 
246
        char *node_scope, *p;
 
247
        xmlNodePtr node;
 
248
 
 
249
        strncpy(accel_scope, accel->spec, 255);
 
250
        if ((p = strrchr(accel_scope, '/')))
 
251
                p[1] = '\0';
 
252
        if (strcmp(scope, accel_scope) == 0)
 
253
                return root;
 
254
 
 
255
#ifndef xmlChildrenNode
 
256
        node = root->childs;
 
257
#else
 
258
        node = root->xmlChildrenNode;
 
259
#endif
 
260
        while (node) {
 
261
                if (strcmp(node->name, "scope") == 0) {
 
262
                        node_scope = xmlGetProp(node, "scope");
 
263
                        if (!node_scope)
 
264
                                return NULL;
 
265
                        snprintf(combined_scope, 255,
 
266
                                 "%s%s/", scope, node_scope);
 
267
                        if (strncmp(combined_scope, accel_scope,
 
268
                                    strlen(combined_scope)) == 0)
 
269
                                break;
 
270
                } else
 
271
                        /* Ignore */ ;
 
272
 
 
273
                node = node->next;
 
274
        }
 
275
 
 
276
        if (!node) {
 
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,
 
283
                         node_scope);
 
284
        }
 
285
 
 
286
        return getScopeNode(node, combined_scope, accel);
 
287
}
 
288
 
 
289
xmlDocPtr glame_accels_to_xml()
 
290
{
 
291
        xmlDocPtr doc;
 
292
        xmlNodePtr docroot, scope, entry;
 
293
        struct accel *accel, *dummy;
 
294
        char s[256];
 
295
 
 
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);
 
305
        }
 
306
        xmlDocSetRootElement(doc, docroot);
 
307
 
 
308
        return doc;
 
309
}
 
310
 
 
311
 
 
312
static void _free_accel(struct accel *accel)
 
313
{
 
314
        hash_remove_accel(accel);
 
315
        glame_list_del(&accel->list);
 
316
        free(accel->spec);
 
317
        free(accel->action);
 
318
        free(accel);
 
319
}
 
320
 
 
321
int glame_accel_add(const char *spec, guint state_mask, guint state,
 
322
                    const char *action)
 
323
{
 
324
        struct accel *accel, *old;
 
325
 
 
326
        if (!spec || !action)
 
327
                return -1;
 
328
 
 
329
        if (!(accel = ALLOC(struct accel)))
 
330
                return -1;
 
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);
 
337
 
 
338
        if ((old = hash_find_accel(spec, state)))
 
339
                _free_accel(old);
 
340
 
 
341
        glame_list_add(&accel->list, &_glame_accel_list);
 
342
        hash_add_accel(accel);
 
343
 
 
344
        return 0;
 
345
}
 
346
 
 
347
void glame_accel_del(const char *spec, guint state)
 
348
{
 
349
        struct accel *accel;
 
350
 
 
351
        if (!(accel = hash_find_accel(spec, state)))
 
352
                return;
 
353
        _free_accel(accel);
 
354
}
 
355
 
 
356
void glame_accel_del_all(const char *scope)
 
357
{
 
358
        struct accel *accel, *dummy;
 
359
        int len;
 
360
 
 
361
        len = strlen(scope);
 
362
        glame_list_safe_foreach(&_glame_accel_list, struct accel, list, dummy, accel) {
 
363
                if (strncmp(accel->spec, scope, len) == 0)
 
364
                        _free_accel(accel);
 
365
        }
 
366
}
 
367
 
 
368
 
 
369
guint glame_accel_install(GtkWidget *widget,
 
370
                          const char *scope, ...)
 
371
{
 
372
        struct accel_cb_data *data;
 
373
        va_list va;
 
374
        guint handler;
 
375
 
 
376
        if (!widget || !scope)
 
377
                return 0;
 
378
 
 
379
        va_start(va, scope);
 
380
        if (va_arg(va, const char *) != NULL) {
 
381
                va_end(va);
 
382
                return 0; /* FIXME: later... */
 
383
        }
 
384
 
 
385
        if (!(data = ALLOC(struct accel_cb_data)))
 
386
                return 0;
 
387
        data->widget = widget;
 
388
        data->scope = strdup(scope);
 
389
 
 
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);
 
394
 
 
395
        return handler;
 
396
}
 
397
 
 
398
 
 
399
guint glame_accel_widget_data_cb(GtkWidget *widget, gpointer spec)
 
400
{
 
401
        struct accel *accel;
 
402
 
 
403
        if (!(accel = hash_find_accel(spec, 0)))
 
404
                return FALSE;
 
405
 
 
406
        glame_gh_safe_eval_str(accel->action);
 
407
 
 
408
        return TRUE;
 
409
}
 
410
 
 
411
 
 
412
GtkWidget *glame_accel_edit_widget(const char *scope, int edit)
 
413
{
 
414
        GtkWidget *sw, *clist;
 
415
        static char *labels[] = { "Scope", "Key", "Binding" };
 
416
        struct accel *accel, *dummy;
 
417
 
 
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);
 
422
 
 
423
        glame_accel_safe_foreach(dummy, accel) {
 
424
                char e_scope[1024];
 
425
                char e_key[1024];
 
426
                char *line[3], *p, *pp;
 
427
 
 
428
                if (strncmp(accel->spec, scope, strlen(scope)) != 0)
 
429
                        continue;
 
430
 
 
431
                /* Create scope */
 
432
                strncpy(e_scope, accel->spec + strlen(scope), 1024);
 
433
                if ((p = strrchr(e_scope, '/'))) {
 
434
                        if (p[1] == '\0')
 
435
                                p--;
 
436
                } else
 
437
                        p = e_scope;
 
438
                *p = '\0';
 
439
 
 
440
                /* Create key */
 
441
                p = e_key;
 
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, '/')))
 
457
                        pp = accel->spec-1;
 
458
                else {
 
459
                        if (pp[1] == '\0')
 
460
                                pp--;
 
461
                }
 
462
                sprintf(p, "%s", pp+1);
 
463
 
 
464
                line[0] = e_scope;
 
465
                line[1] = e_key;
 
466
                line[2] = accel->action;
 
467
 
 
468
                gtk_clist_append(GTK_CLIST(clist), line);
 
469
        }
 
470
 
 
471
        sw = gtk_scrolled_window_new(NULL, NULL);
 
472
        gtk_container_add(GTK_CONTAINER(sw), clist);
 
473
        gtk_widget_set_usize(sw, 500, 300);
 
474
 
 
475
        return sw;
 
476
}
 
477
 
 
478
GtkWidget *glame_accel_edit_dialog(const char *scope, int edit,
 
479
                                   GtkWindow *parent)
 
480
{
 
481
        GtkWidget *dialog, *accel_widget;
 
482
 
 
483
        accel_widget = glame_accel_edit_widget(scope, edit);
 
484
        if (!accel_widget)
 
485
                return NULL;
 
486
 
 
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);
 
491
        if (parent)
 
492
                gnome_dialog_set_parent(GNOME_DIALOG(dialog), parent);
 
493
 
 
494
        gtk_container_add(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox), accel_widget);
 
495
        gtk_widget_show_all(accel_widget);
 
496
 
 
497
        return dialog;
 
498
}
 
499
 
 
500
static SCM gls_glame_accel_edit_dialog_new(SCM s_scope, SCM s_edit)
 
501
{
 
502
        char *scope;
 
503
        int len;
 
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");
 
508
 
 
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)));
 
512
        free(scope);
 
513
 
 
514
        return SCM_UNSPECIFIED;
 
515
}