glade-name-context.c 6.45 KB
Newer Older
1 2 3 4 5
/*
 * glade-name-context.c
 *
 * Copyright (C) 2008 Tristan Van Berkom.
 *
Tristan Van Berkom's avatar
Tristan Van Berkom committed
6 7 8 9
 * This library is free software; you can redistribute it and/or it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
10
 *
Tristan Van Berkom's avatar
Tristan Van Berkom committed
11 12 13 14
 * 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
 * Lesser General Public License for more details.
15
 *
Tristan Van Berkom's avatar
Tristan Van Berkom committed
16 17
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this program; if not, write to the Free Software
18 19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
Tristan Van Berkom's avatar
Tristan Van Berkom committed
20 21 22
 * Authors:
 *   Tristan Van Berkom <tvb@gnome.org>
 *
23 24 25 26 27 28 29 30 31 32 33
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

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

#include "glade-id-allocator.h"
#include "glade-name-context.h"

34 35 36
struct _GladeNameContext
{
  GHashTable *name_allocators;
37

38
  GHashTable *names;
39 40 41 42 43 44 45
};



GladeNameContext *
glade_name_context_new (void)
{
46
  GladeNameContext *context = g_slice_new0 (GladeNameContext);
47

48 49 50 51 52
  context->name_allocators = g_hash_table_new_full (g_str_hash,
                                                    g_str_equal,
                                                    g_free,
                                                    (GDestroyNotify)
                                                    glade_id_allocator_destroy);
53

54 55
  context->names = g_hash_table_new_full (g_str_hash,
                                          g_str_equal, g_free, NULL);
56

57
  return context;
58 59 60
}

void
61
glade_name_context_destroy (GladeNameContext * context)
62
{
63
  g_return_if_fail (context != NULL);
64

65 66
  g_hash_table_destroy (context->name_allocators);
  g_hash_table_destroy (context->names);
67
  g_slice_free (GladeNameContext, context);
68 69 70
}

gchar *
71 72
glade_name_context_new_name (GladeNameContext * context,
                             const gchar * base_name)
73
{
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
  GladeIDAllocator *id_allocator;
  const gchar *number;
  gchar *name = NULL, *freeme = NULL;
  guint i = 1;

  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (base_name && base_name[0], NULL);

  number = base_name + strlen (base_name);
  while (number > base_name && g_ascii_isdigit (number[-1]))
    --number;

  if (*number)
    {
      freeme = g_strndup (base_name, number - base_name);
      base_name = freeme;
    }

  id_allocator = g_hash_table_lookup (context->name_allocators, base_name);

  if (id_allocator == NULL)
    {
      id_allocator = glade_id_allocator_new ();
      g_hash_table_insert (context->name_allocators,
                           g_strdup (base_name), id_allocator);
    }

  do
    {
      g_free (name);
      i = glade_id_allocator_allocate (id_allocator);
      name = g_strdup_printf ("%s%u", base_name, i);
    }
  while (glade_name_context_has_name (context, name));

  g_free (freeme);
  return name;
111 112 113
}

gchar *
114 115 116
glade_name_context_dual_new_name (GladeNameContext * context,
                                  GladeNameContext * another_context,
                                  const gchar * base_name)
117
{
118
  GladeIDAllocator *id_allocator;
119 120 121 122 123
  GList            *free_ids = NULL, *l;
  const gchar      *number;
  gchar            *name = NULL, *freeme = NULL;
  guint             i;
  gboolean          found = FALSE;
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (another_context != NULL, NULL);
  g_return_val_if_fail (base_name && base_name[0], NULL);

  number = base_name + strlen (base_name);
  while (number > base_name && g_ascii_isdigit (number[-1]))
    --number;

  if (*number)
    {
      freeme = g_strndup (base_name, number - base_name);
      base_name = freeme;
    }

  id_allocator = g_hash_table_lookup (context->name_allocators, base_name);

  if (id_allocator == NULL)
    {
      id_allocator = glade_id_allocator_new ();
      g_hash_table_insert (context->name_allocators,
                           g_strdup (base_name), id_allocator);
    }

148
  while (!found)
149 150 151 152
    {
      g_free (name);
      i = glade_id_allocator_allocate (id_allocator);
      name = g_strdup_printf ("%s%u", base_name, i);
153 154 155 156 157 158

      if (!(glade_name_context_has_name (context, name) ||
	    glade_name_context_has_name (another_context, name)))
	found = TRUE;
      else
	free_ids = g_list_prepend (free_ids, GUINT_TO_POINTER (i));
159
    }
160 161 162 163 164 165 166 167 168

  /* Release all the ids that were not hits */
  for (l = free_ids; l; l = l->next)
    {
      i = GPOINTER_TO_UINT (l->data);
      
      glade_id_allocator_release (id_allocator, i);
    }
  g_list_free (free_ids);
169 170 171

  g_free (freeme);
  return name;
172 173 174
}

guint
175
glade_name_context_n_names (GladeNameContext * context)
176
{
177
  g_return_val_if_fail (context != NULL, FALSE);
178

179
  return g_hash_table_size (context->names);
180 181 182
}

gboolean
183
glade_name_context_has_name (GladeNameContext * context, const gchar * name)
184
{
185 186
  g_return_val_if_fail (context != NULL, FALSE);
  g_return_val_if_fail (name && name[0], FALSE);
187

188
  return (g_hash_table_lookup (context->names, name) != NULL);
189 190 191
}

gboolean
192
glade_name_context_add_name (GladeNameContext * context, const gchar * name)
193
{
194 195 196 197 198 199 200 201 202 203 204 205 206
  gboolean ret = FALSE;

  g_return_val_if_fail (context != NULL, FALSE);
  g_return_val_if_fail (name && name[0], FALSE);

  if (!glade_name_context_has_name (context, name))
    {
      g_hash_table_insert (context->names, g_strdup (name),
                           GINT_TO_POINTER (TRUE));
      ret = TRUE;
    }

  return ret;
207 208 209
}

void
210
glade_name_context_release_name (GladeNameContext * context, const gchar * name)
211 212
{

213 214 215 216 217 218 219 220
  const gchar *first_number = name;
  gchar *end_number, *base_name;
  GladeIDAllocator *id_allocator;
  gunichar ch;
  gint id;

  g_return_if_fail (context != NULL);
  g_return_if_fail (name && name[0]);
221

222 223
  /* Remove from name hash first... */
  g_hash_table_remove (context->names, name);
224

225 226 227
  do
    {
      ch = g_utf8_get_char (first_number);
228

229 230
      if (ch == 0 || g_unichar_isdigit (ch))
        break;
231

232 233 234
      first_number = g_utf8_next_char (first_number);
    }
  while (TRUE);
235

236 237 238
  /* if there is a number - then we have to unallocate it... */
  if (ch == 0)
    return;
239

240 241
  base_name = g_strdup (name);
  *(base_name + (first_number - name)) = 0;
242

243 244 245 246 247
  if ((id_allocator =
       g_hash_table_lookup (context->name_allocators, base_name)) != NULL)
    {
      id = (int) strtol (first_number, &end_number, 10);
      if (*end_number == 0)
248
	glade_id_allocator_release (id_allocator, id);
249
    }
250

251
  g_free (base_name);
252
}