gtkaccelgroup.c 33.7 KB
Newer Older
Tim Janik's avatar
Tim Janik committed
1
/* GTK - The GIMP Toolkit
2
 * Copyright (C) 1998, 2001 Tim Janik
Tim Janik's avatar
Tim Janik committed
3 4
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Tim Janik's avatar
Tim Janik committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
12
 * Lesser General Public License for more details.
Tim Janik's avatar
Tim Janik committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Tim Janik's avatar
Tim Janik committed
15 16 17 18
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */
Tim Janik's avatar
Tim Janik committed
26
#include "gtkaccelgroup.h"
27
#include "gtkaccelmap.h"
Tim Janik's avatar
Tim Janik committed
28
#include "gdk/gdkkeysyms.h"
29
#include "gtkmarshalers.h"
Tim Janik's avatar
Tim Janik committed
30
#include "gtksignal.h"
31 32 33 34 35 36 37 38 39 40

#include <string.h>
#include <stdlib.h>


/* --- prototypes --- */
static void gtk_accel_group_class_init	(GtkAccelGroupClass	*class);
static void gtk_accel_group_init	(GtkAccelGroup		*accel_group);
static void gtk_accel_group_finalize	(GObject		*object);

Tim Janik's avatar
Tim Janik committed
41 42

/* --- variables --- */
43 44 45 46
static GObjectClass     *parent_class = NULL;
static guint		 signal_accel_activate = 0;
static guint		 signal_accel_changed = 0;
static guint		 quark_acceleratable_groups = 0;
Tim Janik's avatar
Tim Janik committed
47 48 49
static guint		 default_accel_mod_mask = (GDK_SHIFT_MASK |
						   GDK_CONTROL_MASK |
						   GDK_MOD1_MASK);
50

Tim Janik's avatar
Tim Janik committed
51 52

/* --- functions --- */
53
/**
Matthias Clasen's avatar
Matthias Clasen committed
54 55
 * gtk_accel_group_get_type:
 * @returns: the type ID for accelerator groups.
56
 */
57 58 59 60 61 62 63
GType
gtk_accel_group_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    {
64
      static const GTypeInfo object_info = {
65 66 67 68 69 70
	sizeof (GtkAccelGroupClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gtk_accel_group_class_init,
	NULL,   /* clas_finalize */
	NULL,   /* class_data */
71
	sizeof (GtkAccelGroup),
72 73 74 75 76 77 78 79 80 81 82 83
	0,      /* n_preallocs */
	(GInstanceInitFunc) gtk_accel_group_init,
      };

      object_type = g_type_register_static (G_TYPE_OBJECT,
					    "GtkAccelGroup",
					    &object_info, 0);
    }

  return object_type;
}

84 85 86 87 88
static gboolean
accel_activate_accumulator (GSignalInvocationHint *ihint,
			    GValue                *return_accu,
			    const GValue          *handler_return,
			    gpointer               data)
89
{
90 91
  gboolean continue_emission;
  gboolean handler_val;
92

93 94
  /* handler returns whether the accelerator was handled */
  handler_val = g_value_get_boolean (handler_return);
95

96 97
  /* record that as result for this emission */
  g_value_set_boolean (return_accu, handler_val);
98

99 100
  /* don't continue if accelerator was handled */
  continue_emission = !handler_val;
101

102
  return continue_emission;
103 104 105
}

static void
106
gtk_accel_group_class_init (GtkAccelGroupClass *class)
Tim Janik's avatar
Tim Janik committed
107
{
108 109 110 111 112 113 114 115 116 117 118 119 120 121
  GObjectClass *object_class = G_OBJECT_CLASS (class);

  parent_class = g_type_class_peek_parent (class);

  quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");

  object_class->finalize = gtk_accel_group_finalize;

  class->accel_changed = NULL;
  signal_accel_activate = g_signal_new ("accel_activate",
					G_OBJECT_CLASS_TYPE (class),
					G_SIGNAL_DETAILED,
					0,
					accel_activate_accumulator, NULL,
122
					_gtk_marshal_BOOLEAN__OBJECT_UINT_UINT,
123 124 125 126 127 128
					G_TYPE_BOOLEAN, 3, G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT);
  signal_accel_changed = g_signal_new ("accel_changed",
				       G_OBJECT_CLASS_TYPE (class),
				       G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
				       G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
				       NULL, NULL,
129
				       _gtk_marshal_VOID__UINT_UINT_BOXED,
130
				       G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_CLOSURE);
131 132
}

133 134
static void
gtk_accel_group_finalize (GObject *object)
135
{
136
  GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
137 138 139 140 141 142 143 144 145 146 147 148 149
  guint i;
  
  for (i = 0; i < accel_group->n_accels; i++)
    {
      GtkAccelGroupEntry *entry = &accel_group->priv_accels[i];

      if (entry->accel_path_quark)
	{
	  const gchar *accel_path = g_quark_to_string (entry[i].accel_path_quark);

	  _gtk_accel_map_remove_group (accel_path, accel_group);
	}
    }
150

151
  g_free (accel_group->priv_accels);
152

153
  G_OBJECT_CLASS (parent_class)->finalize (object);
Tim Janik's avatar
Tim Janik committed
154 155
}

156 157
static void
gtk_accel_group_init (GtkAccelGroup *accel_group)
Tim Janik's avatar
Tim Janik committed
158
{
159 160 161 162 163
  accel_group->lock_count = 0;
  accel_group->modifier_mask = gtk_accelerator_get_default_mod_mask ();
  accel_group->acceleratables = NULL;
  accel_group->n_accels = 0;
  accel_group->priv_accels = NULL;
Tim Janik's avatar
Tim Janik committed
164 165
}

166
/**
Matthias Clasen's avatar
Matthias Clasen committed
167
 * gtk_accel_group_new:
168
 * @returns: a new #GtkAccelGroup object
169
 * 
170 171
 * Creates a new #GtkAccelGroup. 
 */
Tim Janik's avatar
Tim Janik committed
172
GtkAccelGroup*
173
gtk_accel_group_new (void)
Tim Janik's avatar
Tim Janik committed
174
{
175
  return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL);
Tim Janik's avatar
Tim Janik committed
176 177 178
}

static void
179 180
accel_group_weak_ref_detach (GSList  *free_list,
			     GObject *stale_object)
Tim Janik's avatar
Tim Janik committed
181
{
182
  GSList *slist;
Tim Janik's avatar
Tim Janik committed
183 184 185 186 187 188
  
  for (slist = free_list; slist; slist = slist->next)
    {
      GtkAccelGroup *accel_group;
      
      accel_group = slist->data;
189
      accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, stale_object);
190
      g_object_unref (accel_group);
Tim Janik's avatar
Tim Janik committed
191 192 193 194 195
    }
  g_slist_free (free_list);
}

void
196 197
_gtk_accel_group_attach (GtkAccelGroup *accel_group,
			 GObject       *object)
Tim Janik's avatar
Tim Janik committed
198 199 200
{
  GSList *slist;
  
201
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
202
  g_return_if_fail (G_IS_OBJECT (object));
203
  g_return_if_fail (g_slist_find (accel_group->acceleratables, object) == NULL);
Tim Janik's avatar
Tim Janik committed
204
  
205
  g_object_ref (accel_group);
206 207
  accel_group->acceleratables = g_slist_prepend (accel_group->acceleratables, object);
  slist = g_object_get_qdata (object, quark_acceleratable_groups);
208
  if (slist)
209 210 211
    g_object_weak_unref (object,
			 (GWeakNotify) accel_group_weak_ref_detach,
			 slist);
Tim Janik's avatar
Tim Janik committed
212
  slist = g_slist_prepend (slist, accel_group);
213 214 215 216
  g_object_set_qdata (object, quark_acceleratable_groups, slist);
  g_object_weak_ref (object,
		     (GWeakNotify) accel_group_weak_ref_detach,
		     slist);
Tim Janik's avatar
Tim Janik committed
217 218 219
}

void
220 221
_gtk_accel_group_detach (GtkAccelGroup *accel_group,
			 GObject       *object)
Tim Janik's avatar
Tim Janik committed
222 223 224
{
  GSList *slist;
  
225
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
226
  g_return_if_fail (G_IS_OBJECT (object));
227
  g_return_if_fail (g_slist_find (accel_group->acceleratables, object) != NULL);
Tim Janik's avatar
Tim Janik committed
228
  
229 230 231 232 233
  accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, object);
  slist = g_object_get_qdata (object, quark_acceleratable_groups);
  g_object_weak_unref (object,
		       (GWeakNotify) accel_group_weak_ref_detach,
		       slist);
Tim Janik's avatar
Tim Janik committed
234
  slist = g_slist_remove (slist, accel_group);
235
  g_object_set_qdata (object, quark_acceleratable_groups, slist);
236
  if (slist)
237 238 239 240 241 242
    g_object_weak_ref (object,
		       (GWeakNotify) accel_group_weak_ref_detach,
		       slist);
  g_object_unref (accel_group);
}

243 244 245 246 247 248 249
/**
 * gtk_accel_groups_from_object:
 * @object:        a #GObject, usually a #GtkWindow 
 * @returns: a list of all accel groups which are attached to @object
 *
 * Gets a list of all accel groups which are attached to @object.
 */
250
GSList*
251
gtk_accel_groups_from_object (GObject *object)
252 253 254 255 256 257
{
  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
  
  return g_object_get_qdata (object, quark_acceleratable_groups);
}

258 259 260 261 262 263 264 265
/**
 * gtk_accel_group_find:
 * @accel_group: a #GtkAccelGroup
 * @find_func: a function to filter the entries of @accel_group with
 * @data: data to pass to @find_func
 * @returns: the key of the first entry passing @find_func. The key is 
 * owned by GTK+ and must not be freed.
 *
Matthias Clasen's avatar
Matthias Clasen committed
266 267
 * Finds the first entry in an accelerator group for which 
 * @find_func returns %TRUE and returns its #GtkAccelKey.
268 269
 *
 */
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
GtkAccelKey*
gtk_accel_group_find (GtkAccelGroup  *accel_group,
		      gboolean (*find_func) (GtkAccelKey *key,
					     GClosure    *closure,
					     gpointer     data),
		      gpointer        data)
{
  GtkAccelKey *key = NULL;
  guint i;

  g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
  g_return_val_if_fail (find_func != NULL, NULL);

  g_object_ref (accel_group);
  for (i = 0; i < accel_group->n_accels; i++)
    if (find_func (&accel_group->priv_accels[i].key,
		   accel_group->priv_accels[i].closure,
		   data))
      {
	key = &accel_group->priv_accels[i].key;
	break;
      }
  g_object_unref (accel_group);

  return key;
Tim Janik's avatar
Tim Janik committed
295 296
}

297
/**
Matthias Clasen's avatar
Matthias Clasen committed
298
 * gtk_accel_group_lock:
299 300
 * @accel_group: a #GtkAccelGroup
 * 
Matthias Clasen's avatar
Matthias Clasen committed
301 302
 * Locks the given accelerator group.
 *
303 304 305
 * Locking an acelerator group prevents the accelerators contained
 * within it to be changed during runtime. Refer to
 * gtk_accel_map_change_entry() about runtime accelerator changes.
306 307 308 309
 *
 * If called more than once, @accel_group remains locked until
 * gtk_accel_group_unlock() has been called an equivalent number
 * of times.
310
 */
Tim Janik's avatar
Tim Janik committed
311
void
312
gtk_accel_group_lock (GtkAccelGroup *accel_group)
Tim Janik's avatar
Tim Janik committed
313
{
314
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
Tim Janik's avatar
Tim Janik committed
315 316 317 318
  
  accel_group->lock_count += 1;
}

319
/**
Matthias Clasen's avatar
Matthias Clasen committed
320
 * gtk_accel_group_unlock:
321 322
 * @accel_group: a #GtkAccelGroup
 * 
Matthias Clasen's avatar
Matthias Clasen committed
323
 * Undoes the last call to gtk_accel_group_lock() on this @accel_group.
324
 */
Tim Janik's avatar
Tim Janik committed
325
void
326
gtk_accel_group_unlock (GtkAccelGroup *accel_group)
Tim Janik's avatar
Tim Janik committed
327
{
328 329
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
  g_return_if_fail (accel_group->lock_count > 0);
Tim Janik's avatar
Tim Janik committed
330

331
  accel_group->lock_count -= 1;
Tim Janik's avatar
Tim Janik committed
332 333
}

334
static void
335 336
accel_closure_invalidate (gpointer  data,
			  GClosure *closure)
Tim Janik's avatar
Tim Janik committed
337
{
338 339 340
  GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data);

  gtk_accel_group_disconnect (accel_group, closure);
Tim Janik's avatar
Tim Janik committed
341 342
}

343 344 345
static int
bsearch_compare_accels (const void *d1,
			const void *d2)
Tim Janik's avatar
Tim Janik committed
346
{
347 348 349 350 351 352 353
  const GtkAccelGroupEntry *entry1 = d1;
  const GtkAccelGroupEntry *entry2 = d2;

  if (entry1->key.accel_key == entry2->key.accel_key)
    return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods;
  else
    return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1;
Tim Janik's avatar
Tim Janik committed
354 355
}

356 357 358 359 360 361 362
static void
quick_accel_add (GtkAccelGroup  *accel_group,
		 guint           accel_key,
		 GdkModifierType accel_mods,
		 GtkAccelFlags   accel_flags,
		 GClosure       *closure,
		 GQuark          path_quark)
Tim Janik's avatar
Tim Janik committed
363
{
364 365 366
  guint pos, i = accel_group->n_accels++;
  GtkAccelGroupEntry key;

367
  /* find position */
368 369 370 371 372
  key.key.accel_key = accel_key;
  key.key.accel_mods = accel_mods;
  for (pos = 0; pos < i; pos++)
    if (bsearch_compare_accels (&key, accel_group->priv_accels + pos) < 0)
      break;
373 374

  /* insert at position, ref closure */
375 376 377 378 379 380 381 382 383
  accel_group->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv_accels, accel_group->n_accels);
  g_memmove (accel_group->priv_accels + pos + 1, accel_group->priv_accels + pos,
	     (i - pos) * sizeof (accel_group->priv_accels[0]));
  accel_group->priv_accels[pos].key.accel_key = accel_key;
  accel_group->priv_accels[pos].key.accel_mods = accel_mods;
  accel_group->priv_accels[pos].key.accel_flags = accel_flags;
  accel_group->priv_accels[pos].closure = g_closure_ref (closure);
  accel_group->priv_accels[pos].accel_path_quark = path_quark;
  g_closure_sink (closure);
384 385 386 387 388 389 390 391 392 393 394 395 396
  
  /* handle closure invalidation and reverse lookups */
  g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate);

  /* get accel path notification */
  if (path_quark)
    _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group);

  /* connect and notify changed */
  if (accel_key)
    {
      gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
      GQuark accel_quark = g_quark_from_string (accel_name);
397

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
      g_free (accel_name);
      
      /* setup handler */
      g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE);
      
      /* and notify */
      g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
    }
}

static void
quick_accel_remove (GtkAccelGroup      *accel_group,
		    GtkAccelGroupEntry *entry)
{
  guint pos = entry - accel_group->priv_accels;
  GQuark accel_quark = 0;
  guint accel_key = entry->key.accel_key;
  GdkModifierType accel_mods = entry->key.accel_mods;
  GClosure *closure = entry->closure;

  /* quark for notification */
  if (accel_key)
    {
      gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);

      accel_quark = g_quark_from_string (accel_name);
      g_free (accel_name);
    }

  /* clean up closure invalidate notification and disconnect */
  g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
  if (accel_quark)
    g_signal_handlers_disconnect_matched (accel_group,
					  G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE,
					  signal_accel_activate, accel_quark,
					  closure, NULL, NULL);
  /* clean up accel path notification */
  if (entry->accel_path_quark)
    _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group);

  /* physically remove */
  accel_group->n_accels -= 1;
  g_memmove (entry, entry + 1,
	     (accel_group->n_accels - pos) * sizeof (accel_group->priv_accels[0]));

  /* and notify */
  if (accel_quark)
    g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);

  /* remove quick_accel_add() refcount */
  g_closure_unref (closure);
Tim Janik's avatar
Tim Janik committed
449 450
}

451 452 453 454 455
static GtkAccelGroupEntry*
quick_accel_find (GtkAccelGroup  *accel_group,
		  guint           accel_key,
		  GdkModifierType accel_mods,
		  guint		 *count_p)
Tim Janik's avatar
Tim Janik committed
456
{
457 458 459 460 461 462 463 464 465 466
  GtkAccelGroupEntry *entry;
  GtkAccelGroupEntry key;

  if (!accel_group->n_accels)
    return NULL;

  key.key.accel_key = accel_key;
  key.key.accel_mods = accel_mods;
  entry = bsearch (&key, accel_group->priv_accels, accel_group->n_accels,
		   sizeof (accel_group->priv_accels[0]), bsearch_compare_accels);
Tim Janik's avatar
Tim Janik committed
467
  
468 469 470 471 472 473 474 475 476 477 478 479 480 481
  if (!entry)
    return NULL;

  /* step back to the first member */
  for (; entry > accel_group->priv_accels; entry--)
    if (entry[-1].key.accel_key != accel_key ||
	entry[-1].key.accel_mods != accel_mods)
      break;
  /* count equal members */
  for (*count_p = 0; entry + *count_p < accel_group->priv_accels + accel_group->n_accels; (*count_p)++)
    if (entry[*count_p].key.accel_key != accel_key ||
	entry[*count_p].key.accel_mods != accel_mods)
      break;
  return entry;
Tim Janik's avatar
Tim Janik committed
482 483
}

484
/**
Matthias Clasen's avatar
Matthias Clasen committed
485 486
 * gtk_accel_group_connect:
 * @accel_group:      the accelerator group to install an accelerator in
487 488 489 490
 * @accel_key:        key value of the accelerator
 * @accel_mods:       modifier combination of the accelerator
 * @accel_flags:      a flag mask to configure this accelerator
 * @closure:          closure to be executed upon accelerator activation
491
 *
Matthias Clasen's avatar
Matthias Clasen committed
492
 * Installs an accelerator in this group. When @accel_group is being activated
493 494 495
 * in response to a call to gtk_accel_groups_activate(), @closure will be
 * invoked if the @accel_key and @accel_mods from gtk_accel_groups_activate()
 * match those of this connection.
Matthias Clasen's avatar
Matthias Clasen committed
496
 *
497
 * The signature used for the @closure is that of #GtkAccelGroupActivate.
Matthias Clasen's avatar
Matthias Clasen committed
498
 * 
499 500
 * Note that, due to implementation details, a single closure can only be
 * connected to one accelerator group.
501
 */
Tim Janik's avatar
Tim Janik committed
502
void
503 504 505 506
gtk_accel_group_connect (GtkAccelGroup	*accel_group,
			 guint		 accel_key,
			 GdkModifierType accel_mods,
			 GtkAccelFlags	 accel_flags,
507
			 GClosure	*closure)
Tim Janik's avatar
Tim Janik committed
508
{
509 510 511
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
  g_return_if_fail (closure != NULL);
  g_return_if_fail (accel_key > 0);
512
  g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL);
513

514 515 516 517
  g_object_ref (accel_group);
  if (!closure->is_invalid)
    quick_accel_add (accel_group, accel_key, accel_mods, accel_flags, closure, 0);
  g_object_unref (accel_group);
518 519
}

520
/**
Matthias Clasen's avatar
Matthias Clasen committed
521 522
 * gtk_accel_group_connect_by_path:
 * @accel_group:      the accelerator group to install an accelerator in
523 524 525
 * @accel_path:       path used for determining key and modifiers.
 * @closure:          closure to be executed upon accelerator activation
 *
Matthias Clasen's avatar
Matthias Clasen committed
526 527
 * Installs an accelerator in this group, using an accelerator path to look
 * up the appropriate key and modifiers (see gtk_accel_map_add_entry()).
528 529 530 531
 * When @accel_group is being activated in response to a call to
 * gtk_accel_groups_activate(), @closure will be invoked if the @accel_key and
 * @accel_mods from gtk_accel_groups_activate() match the key and modifiers
 * for the path.
Matthias Clasen's avatar
Matthias Clasen committed
532
 *
533 534 535 536 537 538
 * The signature used for the @closure is that of #GtkAccelGroupActivate.
 */
void
gtk_accel_group_connect_by_path (GtkAccelGroup	*accel_group,
				 const gchar    *accel_path,
				 GClosure	*closure)
539
{
540 541 542
  guint accel_key = 0;
  GdkModifierType accel_mods = 0;
  GtkAccelKey key;
543

544 545 546
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
  g_return_if_fail (closure != NULL);
  g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
547

548 549
  if (closure->is_invalid)
    return;
550

551
  g_object_ref (accel_group);
552

553 554 555 556 557 558 559 560
  if (gtk_accel_map_lookup_entry (accel_path, &key))
    {
      accel_key = key.accel_key;
      accel_mods = key.accel_mods;
    }

  quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
		   g_quark_from_string (accel_path));
561

562
  g_object_unref (accel_group);
Tim Janik's avatar
Tim Janik committed
563 564
}

565
/**
Matthias Clasen's avatar
Matthias Clasen committed
566
 * gtk_accel_group_disconnect:
567 568 569 570
 * @accel_group: the accelerator group to remove an accelerator from
 * @closure:     the closure to remove from this accelerator group
 * @returns:     %TRUE if the closure was found and got disconnected
 *
Matthias Clasen's avatar
Matthias Clasen committed
571
 * Removes an accelerator previously installed through
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
 * gtk_accel_group_connect().
 */
gboolean
gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
			    GClosure      *closure)
{
  guint i;

  g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);

  for (i = 0; i < accel_group->n_accels; i++)
    if (accel_group->priv_accels[i].closure == closure)
      {
	g_object_ref (accel_group);
	quick_accel_remove (accel_group, accel_group->priv_accels + i);
	g_object_unref (accel_group);
	return TRUE;
      }
  return FALSE;
}

/**
Matthias Clasen's avatar
Matthias Clasen committed
594 595
 * gtk_accel_group_disconnect_key:
 * @accel_group:      the accelerator group to install an accelerator in
596 597
 * @accel_key:        key value of the accelerator
 * @accel_mods:       modifier combination of the accelerator
Matthias Clasen's avatar
Matthias Clasen committed
598 599
 * @returns:          %TRUE if there was an accelerator which could be 
 *                    removed, %FALSE otherwise
600
 *
Matthias Clasen's avatar
Matthias Clasen committed
601
 * Removes an accelerator previously installed through
602 603 604
 * gtk_accel_group_connect().
 */
gboolean
605 606 607
gtk_accel_group_disconnect_key (GtkAccelGroup  *accel_group,
				guint	        accel_key,
				GdkModifierType accel_mods)
Tim Janik's avatar
Tim Janik committed
608
{
609 610 611 612 613
  GtkAccelGroupEntry *entries;
  GSList *slist, *clist = NULL;
  gboolean removed_one = FALSE;
  guint n;

614 615
  g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
  g_object_ref (accel_group);
  
  entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
  while (n--)
    {
      GClosure *closure = g_closure_ref (entries[n].closure);

      clist = g_slist_prepend (clist, closure);
    }

  for (slist = clist; slist; slist = slist->next)
    {
      GClosure *closure = slist->data;

      removed_one |= gtk_accel_group_disconnect (accel_group, closure);
      g_closure_unref (closure);
    }
  g_slist_free (clist);

  g_object_unref (accel_group);

  return removed_one;
}

void
_gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
			    GQuark         accel_path_quark)
{
  GSList *slist, *clist = NULL;
  guint i;

  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));

  g_object_ref (accel_group);

  for (i = 0; i < accel_group->n_accels; i++)
    if (accel_group->priv_accels[i].accel_path_quark == accel_path_quark)
      {
	GClosure *closure = g_closure_ref (accel_group->priv_accels[i].closure);

	clist = g_slist_prepend (clist, closure);
      }

  for (slist = clist; slist; slist = slist->next)
    {
      GClosure *closure = slist->data;

      gtk_accel_group_disconnect (accel_group, closure);
      gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
      g_closure_unref (closure);
    }
  g_slist_free (clist);

  g_object_unref (accel_group);
Tim Janik's avatar
Tim Janik committed
670 671
}

672 673 674 675 676 677 678 679 680 681 682
/**
 * gtk_accel_group_query:
 * @accel_group:      the accelerator group to query
 * @accel_key:        key value of the accelerator
 * @accel_mods:       modifier combination of the accelerator
 * @n_entries:        location to return the number of entries found, or %NULL
 * @returns:          an array of @n_entries #GtkAccelGroupEntry elements, or %NULL. The array is owned by GTK+ and must not be freed. 
 *
 * Queries an accelerator group for all entries matching @accel_key and 
 * @accel_mods.
 */
683 684 685 686 687
GtkAccelGroupEntry*
gtk_accel_group_query (GtkAccelGroup  *accel_group,
		       guint           accel_key,
		       GdkModifierType accel_mods,
		       guint          *n_entries)
Tim Janik's avatar
Tim Janik committed
688
{
689 690
  GtkAccelGroupEntry *entries;
  guint n;
Tim Janik's avatar
Tim Janik committed
691

692 693 694 695 696 697 698 699
  g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);

  entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);

  if (n_entries)
    *n_entries = entries ? n : 0;

  return entries;
Tim Janik's avatar
Tim Janik committed
700 701
}

702 703 704 705 706 707 708 709
/**
 * gtk_accel_group_from_accel_closure:
 * @closure: a #GClosure
 * @returns: the #GtkAccelGroup to which @closure is connected, or %NULL.
 *
 * Finds the #GtkAccelGroup to which @closure is connected; 
 * see gtk_accel_group_connect().
 */
710 711
GtkAccelGroup*
gtk_accel_group_from_accel_closure (GClosure *closure)
Tim Janik's avatar
Tim Janik committed
712
{
713 714 715 716
  guint i;

  g_return_val_if_fail (closure != NULL, NULL);

717
  /* a few remarks on wat we do here. in general, we need a way to reverse lookup
718 719
   * accel_groups from closures that are being used in accel groups. this could
   * be done e.g via a hashtable. it is however cheaper (memory wise) to just
720 721 722
   * use the invalidation notifier on the closure itself (which we need to install
   * anyway), that contains the accel group as data which, besides needing to peek
   * a bit at closure internals, works just as good.
723 724
   */
  for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
725
    if (closure->notifiers[i].notify == accel_closure_invalidate)
726 727 728
      return closure->notifiers[i].data;

  return NULL;
Tim Janik's avatar
Tim Janik committed
729 730
}

731 732 733 734 735 736
gboolean
_gtk_accel_group_activate (GtkAccelGroup  *accel_group,
			   GQuark	   accel_quark,
			   GObject	  *acceleratable,
			   guint	   accel_key,
			   GdkModifierType accel_mods)
Tim Janik's avatar
Tim Janik committed
737
{
738 739 740 741 742 743 744 745 746
  gboolean was_handled;

  g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);

  was_handled = FALSE;
  g_signal_emit (accel_group, signal_accel_activate, accel_quark,
		 acceleratable, accel_key, accel_mods, &was_handled);

  return was_handled;
Tim Janik's avatar
Tim Janik committed
747 748
}

749 750
/**
 * gtk_accel_groups_activate:
751
 * @object:        the #GObject, usually a #GtkWindow, on which
752
 *                 to activate the accelerator.
753 754 755 756 757
 * @accel_key:     accelerator keyval from a key event
 * @accel_mods:    keyboard state mask from a key event
 * @returns:       %TRUE if the accelerator was handled, %FALSE otherwise
 * 
 * Finds the first accelerator in any #GtkAccelGroup attached
758
 * to @object that matches @accel_key and @accel_mods, and
759 760 761 762 763
 * activates that accelerator.
 * If an accelerator was activated and handled this keypress, %TRUE
 * is returned.
 */
gboolean
764
gtk_accel_groups_activate (GObject	  *object,
765 766
			   guint	   accel_key,
			   GdkModifierType accel_mods)
Tim Janik's avatar
Tim Janik committed
767
{
768
  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
Tim Janik's avatar
Tim Janik committed
769
  
770 771 772 773 774
  if (gtk_accelerator_valid (accel_key, accel_mods))
    {
      gchar *accel_name;
      GQuark accel_quark;
      GSList *slist;
Tim Janik's avatar
Tim Janik committed
775

776
      accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
777 778 779
      accel_quark = g_quark_from_string (accel_name);
      g_free (accel_name);
      
780 781
      for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
	if (_gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
782 783
	  return TRUE;
    }
Tim Janik's avatar
Tim Janik committed
784
  
785
  return FALSE;
Tim Janik's avatar
Tim Janik committed
786 787
}

788
/**
Matthias Clasen's avatar
Matthias Clasen committed
789
 * gtk_accelerator_valid:
790
 * @keyval:    a GDK keyval
791
 * @modifiers: modifier mask
792
 * @returns:   %TRUE if the accelerator is valid
793 794
 * 
 * Determines whether a given keyval and modifier mask constitute
Matthias Clasen's avatar
Matthias Clasen committed
795 796
 * a valid keyboard accelerator. For example, the #GDK_a keyval
 * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
797 798 799
 * But by default (see gtk_accelerator_set_default_mod_mask()) you
 * cannot use the NumLock key as an accelerator modifier.
 */
Tim Janik's avatar
Tim Janik committed
800 801
gboolean
gtk_accelerator_valid (guint		  keyval,
802
		       GdkModifierType	  modifiers)
Tim Janik's avatar
Tim Janik committed
803
{
804
  static const guint invalid_accelerator_vals[] = {
Tim Janik's avatar
Tim Janik committed
805 806
    GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
    GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
Owen Taylor's avatar
Owen Taylor committed
807
    GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
Tim Janik's avatar
Tim Janik committed
808 809 810 811 812 813 814 815 816
    GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
    GDK_Scroll_Lock, GDK_Sys_Req, 
    GDK_Up, GDK_Down, GDK_Left, GDK_Right, GDK_Tab, GDK_ISO_Left_Tab,
    GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right, GDK_KP_Tab,
    GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
    GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
    GDK_Terminate_Server, GDK_AudibleBell_Enable,
    0
  };
817
  const guint *ac_val;
Tim Janik's avatar
Tim Janik committed
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912

  modifiers &= GDK_MODIFIER_MASK;
    
  if (keyval <= 0xFF)
    return keyval >= 0x20;

  ac_val = invalid_accelerator_vals;
  while (*ac_val)
    {
      if (keyval == *ac_val++)
	return FALSE;
    }

  return TRUE;
}

static inline gboolean
is_alt (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 'a' || string[1] == 'A') &&
	  (string[2] == 'l' || string[2] == 'L') &&
	  (string[3] == 't' || string[3] == 'T') &&
	  (string[4] == '>'));
}

static inline gboolean
is_ctl (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 'c' || string[1] == 'C') &&
	  (string[2] == 't' || string[2] == 'T') &&
	  (string[3] == 'l' || string[3] == 'L') &&
	  (string[4] == '>'));
}

static inline gboolean
is_modx (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 'm' || string[1] == 'M') &&
	  (string[2] == 'o' || string[2] == 'O') &&
	  (string[3] == 'd' || string[3] == 'D') &&
	  (string[4] >= '1' && string[4] <= '5') &&
	  (string[5] == '>'));
}

static inline gboolean
is_ctrl (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 'c' || string[1] == 'C') &&
	  (string[2] == 't' || string[2] == 'T') &&
	  (string[3] == 'r' || string[3] == 'R') &&
	  (string[4] == 'l' || string[4] == 'L') &&
	  (string[5] == '>'));
}

static inline gboolean
is_shft (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 's' || string[1] == 'S') &&
	  (string[2] == 'h' || string[2] == 'H') &&
	  (string[3] == 'f' || string[3] == 'F') &&
	  (string[4] == 't' || string[4] == 'T') &&
	  (string[5] == '>'));
}

static inline gboolean
is_shift (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 's' || string[1] == 'S') &&
	  (string[2] == 'h' || string[2] == 'H') &&
	  (string[3] == 'i' || string[3] == 'I') &&
	  (string[4] == 'f' || string[4] == 'F') &&
	  (string[5] == 't' || string[5] == 'T') &&
	  (string[6] == '>'));
}

static inline gboolean
is_control (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 'c' || string[1] == 'C') &&
	  (string[2] == 'o' || string[2] == 'O') &&
	  (string[3] == 'n' || string[3] == 'N') &&
	  (string[4] == 't' || string[4] == 'T') &&
	  (string[5] == 'r' || string[5] == 'R') &&
	  (string[6] == 'o' || string[6] == 'O') &&
	  (string[7] == 'l' || string[7] == 'L') &&
	  (string[8] == '>'));
}

913 914 915 916 917 918 919 920 921 922 923 924 925 926
static inline gboolean
is_release (const gchar *string)
{
  return ((string[0] == '<') &&
	  (string[1] == 'r' || string[1] == 'R') &&
	  (string[2] == 'e' || string[2] == 'E') &&
	  (string[3] == 'l' || string[3] == 'L') &&
	  (string[4] == 'e' || string[4] == 'E') &&
	  (string[5] == 'a' || string[5] == 'A') &&
	  (string[6] == 's' || string[6] == 'S') &&
	  (string[7] == 'e' || string[7] == 'E') &&
	  (string[8] == '>'));
}

927
/**
Matthias Clasen's avatar
Matthias Clasen committed
928
 * gtk_accelerator_parse:
929 930 931
 * @accelerator:      string representing an accelerator
 * @accelerator_key:  return location for accelerator keyval
 * @accelerator_mods: return location for accelerator modifier mask
932 933
 *
 * Parses a string representing an accelerator. The
934 935
 * format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1" or
 * "&lt;Release&gt;z" (the last one is for key release).
936
 * The parser is fairly liberal and allows lower or upper case,
937
 * and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
938 939 940
 *
 * If the parse fails, @accelerator_key and @accelerator_mods will
 * be set to 0 (zero).
941
 */
Tim Janik's avatar
Tim Janik committed
942
void
943 944 945
gtk_accelerator_parse (const gchar     *accelerator,
		       guint           *accelerator_key,
		       GdkModifierType *accelerator_mods)
Tim Janik's avatar
Tim Janik committed
946 947
{
  guint keyval;
948
  GdkModifierType mods;
Tim Janik's avatar
Tim Janik committed
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
  gint len;
  
  if (accelerator_key)
    *accelerator_key = 0;
  if (accelerator_mods)
    *accelerator_mods = 0;
  g_return_if_fail (accelerator != NULL);
  
  keyval = 0;
  mods = 0;
  len = strlen (accelerator);
  while (len)
    {
      if (*accelerator == '<')
	{
964 965 966 967 968 969 970
	  if (len >= 9 && is_release (accelerator))
	    {
	      accelerator += 9;
	      len -= 9;
	      mods |= GDK_RELEASE_MASK;
	    }
	  else if (len >= 9 && is_control (accelerator))
Tim Janik's avatar
Tim Janik committed
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995
	    {
	      accelerator += 9;
	      len -= 9;
	      mods |= GDK_CONTROL_MASK;
	    }
	  else if (len >= 7 && is_shift (accelerator))
	    {
	      accelerator += 7;
	      len -= 7;
	      mods |= GDK_SHIFT_MASK;
	    }
	  else if (len >= 6 && is_shft (accelerator))
	    {
	      accelerator += 6;
	      len -= 6;
	      mods |= GDK_SHIFT_MASK;
	    }
	  else if (len >= 6 && is_ctrl (accelerator))
	    {
	      accelerator += 6;
	      len -= 6;
	      mods |= GDK_CONTROL_MASK;
	    }
	  else if (len >= 6 && is_modx (accelerator))
	    {
996
	      static const guint mod_vals[] = {
Tim Janik's avatar
Tim Janik committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
		GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
		GDK_MOD4_MASK, GDK_MOD5_MASK
	      };

	      len -= 6;
	      accelerator += 4;
	      mods |= mod_vals[*accelerator - '1'];
	      accelerator += 2;
	    }
	  else if (len >= 5 && is_ctl (accelerator))
	    {
	      accelerator += 5;
	      len -= 5;
	      mods |= GDK_CONTROL_MASK;
	    }
	  else if (len >= 5 && is_alt (accelerator))
	    {
	      accelerator += 5;
	      len -= 5;
	      mods |= GDK_MOD1_MASK;
	    }
	  else
	    {
	      gchar last_ch;
	      
	      last_ch = *accelerator;
	      while (last_ch && last_ch != '>')
		{
		  last_ch = *accelerator;
		  accelerator += 1;
		  len -= 1;
		}
	    }
	}
      else
	{
	  keyval = gdk_keyval_from_name (accelerator);
	  accelerator += len;
	  len -= len;
	}
    }
  
  if (accelerator_key)
    *accelerator_key = gdk_keyval_to_lower (keyval);
  if (accelerator_mods)
    *accelerator_mods = mods;
}

1045
/**
Matthias Clasen's avatar
Matthias Clasen committed
1046
 * gtk_accelerator_name:
1047 1048
 * @accelerator_key:  accelerator keyval
 * @accelerator_mods: accelerator modifier mask
Matthias Clasen's avatar
Matthias Clasen committed
1049
 * @returns:          a newly-allocated accelerator name
1050 1051 1052
 * 
 * Converts an accelerator keyval and modifier mask
 * into a string parseable by gtk_accelerator_parse().
Matthias Clasen's avatar
Matthias Clasen committed
1053
 * For example, if you pass in #GDK_q and #GDK_CONTROL_MASK,
1054
 * this function returns "&lt;Control&gt;q". 
1055
 *
1056 1057
 * The caller of this function must free the returned string.
 */
Tim Janik's avatar
Tim Janik committed
1058 1059
gchar*
gtk_accelerator_name (guint           accelerator_key,
1060
		      GdkModifierType accelerator_mods)
Tim Janik's avatar
Tim Janik committed
1061
{
1062
  static const gchar text_release[] = "<Release>";
Tim Janik's avatar
Tim Janik committed
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
  static const gchar text_shift[] = "<Shift>";
  static const gchar text_control[] = "<Control>";
  static const gchar text_mod1[] = "<Alt>";
  static const gchar text_mod2[] = "<Mod2>";
  static const gchar text_mod3[] = "<Mod3>";
  static const gchar text_mod4[] = "<Mod4>";
  static const gchar text_mod5[] = "<Mod5>";
  guint l;
  gchar *keyval_name;
  gchar *accelerator;

  accelerator_mods &= GDK_MODIFIER_MASK;

  keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
  if (!keyval_name)
    keyval_name = "";

  l = 0;
1081 1082
  if (accelerator_mods & GDK_RELEASE_MASK)
    l += sizeof (text_release) - 1;
Tim Janik's avatar
Tim Janik committed
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
  if (accelerator_mods & GDK_SHIFT_MASK)
    l += sizeof (text_shift) - 1;
  if (accelerator_mods & GDK_CONTROL_MASK)
    l += sizeof (text_control) - 1;
  if (accelerator_mods & GDK_MOD1_MASK)
    l += sizeof (text_mod1) - 1;
  if (accelerator_mods & GDK_MOD2_MASK)
    l += sizeof (text_mod2) - 1;
  if (accelerator_mods & GDK_MOD3_MASK)
    l += sizeof (text_mod3) - 1;
  if (accelerator_mods & GDK_MOD4_MASK)
    l += sizeof (text_mod4) - 1;
  if (accelerator_mods & GDK_MOD5_MASK)
    l += sizeof (text_mod5) - 1;
  l += strlen (keyval_name);

  accelerator = g_new (gchar, l + 1);

  l = 0;
  accelerator[l] = 0;
1103 1104 1105 1106 1107
  if (accelerator_mods & GDK_RELEASE_MASK)
    {
      strcpy (accelerator + l, text_release);
      l += sizeof (text_release) - 1;
    }
Tim Janik's avatar
Tim Janik committed
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
  if (accelerator_mods & GDK_SHIFT_MASK)
    {
      strcpy (accelerator + l, text_shift);
      l += sizeof (text_shift) - 1;
    }
  if (accelerator_mods & GDK_CONTROL_MASK)
    {
      strcpy (accelerator + l, text_control);
      l += sizeof (text_control) - 1;
    }
  if (accelerator_mods & GDK_MOD1_MASK)
    {
      strcpy (accelerator + l, text_mod1);
      l += sizeof (text_mod1) - 1;
    }
  if (accelerator_mods & GDK_MOD2_MASK)
    {
      strcpy (accelerator + l, text_mod2);
      l += sizeof (text_mod2) - 1;
    }
  if (accelerator_mods & GDK_MOD3_MASK)
    {
      strcpy (accelerator + l, text_mod3);
      l += sizeof (text_mod3) - 1;
    }
  if (accelerator_mods & GDK_MOD4_MASK)
    {
      strcpy (accelerator + l, text_mod4);
      l += sizeof (text_mod4) - 1;
    }
  if (accelerator_mods & GDK_MOD5_MASK)
    {
      strcpy (accelerator + l, text_mod5);
      l += sizeof (text_mod5) - 1;
    }
  strcpy (accelerator + l, keyval_name);

  return accelerator;
}

1148
/**
Matthias Clasen's avatar
Matthias Clasen committed
1149
 * gtk_accelerator_set_default_mod_mask:
1150
 * @default_mod_mask: accelerator modifier mask
1151 1152 1153 1154
 *
 * Sets the modifiers that will be considered significant for keyboard
 * accelerators. The default mod mask is #GDK_CONTROL_MASK |
 * #GDK_SHIFT_MASK | #GDK_MOD1_MASK, that is, Control, Shift, and Alt.
1155
 * Other modifiers will by default be ignored by #GtkAccelGroup.
1156 1157
 *
 * The default mod mask should be changed on application startup,
1158 1159
 * before using any accelerator groups.
 */
Tim Janik's avatar
Tim Janik committed
1160
void
1161
gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
Tim Janik's avatar
Tim Janik committed
1162 1163 1164 1165
{
  default_accel_mod_mask = default_mod_mask & GDK_MODIFIER_MASK;
}

1166
/**
Matthias Clasen's avatar
Matthias Clasen committed
1167
 * gtk_accelerator_get_default_mod_mask:
1168
 * @returns: the default accelerator modifier mask
1169 1170
 *
 * Gets the value set by gtk_accelerator_set_default_mod_mask().
1171
 */
Tim Janik's avatar
Tim Janik committed
1172 1173 1174 1175 1176
guint
gtk_accelerator_get_default_mod_mask (void)
{
  return default_accel_mod_mask;
}