~ubuntu-branches/ubuntu/raring/icon-slicer/raring

« back to all changes in this revision

Viewing changes to .pc/1001_hotspotfix.patch/src/main.c

  • Committer: Package Import Robot
  • Author(s): Jonas Smedegaard
  • Date: 2012-03-14 23:00:39 UTC
  • mfrom: (2.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20120314230039-mfpadkn7b90wd4bd
Tags: 0.3-6
* Modernize CDBS usage: Drop local snippets (all included upstream
  now).
* Use dpkg source format 3.0 (quilt). Stop including patchsys-quilt
  snippet.
* Bump debhelper compat level to 7.
* Bump standards-version to 3.9.3.
* Use anonscm.debian.org URL in Vcs-Browser field.
* Git-ignore quilt .pc dir.
* Update package relations:
  + Build-depend unversioned on libgtk2.0-dev: Needed version
    satisfied even in oldstable.
  + Tighten build-dependency on cdbs: Needed for improved copyright-
    check.
  + Relax build-depend unversioned on debhelper and devscripts: Needed
    versions satisfied even in oldstable.
  + Stop build-depending on quilt or patchutils: Unneeded with source
    format 3.0 (quilt).
  + Build-depend (not only depend) on x11-apps: Needed to build
    example files.
* Append (not early-(re)declare) build-dependencies.
* Extend copyright years of Debian packaging.
* Refresh patch 1001 with shortening quilt options -pab --no-index
  --no-timestamps.
* Suppress copyright-checking binary files, to not upset dpkg-source.
* Rewrite copyright file using format 1.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2003 Red Hat, Inc.
 
3
 *
 
4
 * Permission to use, copy, modify, distribute, and sell this software and its
 
5
 * documentation for any purpose is hereby granted without fee, provided that
 
6
 * the above copyright notice appear in all copies and that both that
 
7
 * copyright notice and this permission notice appear in supporting
 
8
 * documentation, and that the name of Red Hat not be used in advertising or
 
9
 * publicity pertaining to distribution of the software without specific,
 
10
 * written prior permission.  Red Hat makes no representations about the
 
11
 * suitability of this software for any purpose.  It is provided "as is"
 
12
 * without express or implied warranty.
 
13
 *
 
14
 * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 
15
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
 
16
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
17
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 
18
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 
19
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
20
 *
 
21
 * Author:  Owen Taylor, Red Hat, Inc.
 
22
 */
 
23
#include "config.h"
 
24
 
 
25
#include <errno.h>
 
26
#include <stdio.h>
 
27
#include <stdlib.h>
 
28
#include <string.h>
 
29
#include <sys/types.h>
 
30
#include <sys/stat.h>
 
31
#include <unistd.h>
 
32
 
 
33
#include <popt.h>
 
34
 
 
35
#include "themefile.h"
 
36
 
 
37
static void
 
38
theme_rectangle_union (ThemeRectangle *src1,
 
39
                       ThemeRectangle *src2,
 
40
                       ThemeRectangle *dest)
 
41
{
 
42
  int dest_x, dest_y;
 
43
  
 
44
  dest_x = MIN (src1->x, src2->x);
 
45
  dest_y = MIN (src1->y, src2->y);
 
46
  dest->width = MAX (src1->x + src1->width, src2->x + src2->width) - dest_x;
 
47
  dest->height = MAX (src1->y + src1->height, src2->y + src2->height) - dest_y;
 
48
  dest->x = dest_x;
 
49
  dest->y = dest_y;
 
50
}
 
51
 
 
52
static ThemeImage *
 
53
theme_source_find_image (ThemeSource *source,
 
54
                         int          use)
 
55
{
 
56
  GSList *tmp_list;
 
57
 
 
58
  for (tmp_list = source->images; tmp_list; tmp_list = tmp_list->next)
 
59
    {
 
60
      ThemeImage *image = tmp_list->data;
 
61
 
 
62
      if (image->use == use)
 
63
        return image;
 
64
    }
 
65
 
 
66
  return NULL;
 
67
}
 
68
 
 
69
static void
 
70
theme_source_location_start (ThemeSource    *source,
 
71
                             ThemeLocation  *location,
 
72
                             int            *start_x,
 
73
                             int            *start_y)
 
74
{
 
75
  *start_x = (source->margin +
 
76
              location->column * (source->gridsize + source->spacing));
 
77
              
 
78
  *start_y = (source->margin +
 
79
              location->row * (source->gridsize + source->spacing));
 
80
}
 
81
 
 
82
static gboolean
 
83
theme_source_location_bounds (ThemeSource    *source,
 
84
                              ThemeLocation  *location,
 
85
                              int             frame_index,
 
86
                              int             threshold,
 
87
                              gboolean        location_relative,
 
88
                              ThemeRectangle *out)
 
89
{
 
90
  ThemeImage *image = theme_source_find_image (source, frame_index);
 
91
  int start_x, start_y;
 
92
  int i, j;
 
93
  
 
94
  const guchar *pixels;
 
95
  int rowstride;
 
96
  int n_channels;
 
97
 
 
98
  theme_source_location_start (source, location, &start_x, &start_y);
 
99
 
 
100
  rowstride = gdk_pixbuf_get_rowstride (image->image);
 
101
  n_channels = gdk_pixbuf_get_n_channels (image->image);
 
102
 
 
103
  if (n_channels == 3)
 
104
    {
 
105
      out->x = start_x;
 
106
      out->y = start_x;
 
107
      out->width = source->gridsize;
 
108
      out->height = source->gridsize;
 
109
      
 
110
      return TRUE;
 
111
    }
 
112
  else
 
113
    {
 
114
      int min_x = 0, max_x = 0, min_y = 0, max_y = 0;
 
115
      gboolean found = FALSE;
 
116
      
 
117
      pixels = gdk_pixbuf_get_pixels (image->image) + start_y * rowstride + start_x * 4;
 
118
 
 
119
      for (j = 0; j < source->gridsize; j++)
 
120
        {
 
121
          for (i = 0; i < source->gridsize; i++)
 
122
            {
 
123
              if (pixels[4*i + 3] > threshold)
 
124
                {
 
125
                  if (found)
 
126
                    {
 
127
                      if (start_x + i < min_x)
 
128
                        min_x = start_x + i;
 
129
                      if (start_x + i >= max_x)
 
130
                        max_x = start_x + i + 1;
 
131
                      if (start_y + j < min_y)
 
132
                        min_y = start_y + j;
 
133
                      if (start_y + j >= max_y)
 
134
                        max_y = start_y + j + 1;
 
135
                    }
 
136
                  else
 
137
                    {
 
138
                      min_x = start_x + i;
 
139
                      max_x = start_x + i + 1;
 
140
                      min_y = start_y + i;
 
141
                      max_y = start_y + i + 1;
 
142
 
 
143
                      found = TRUE;
 
144
                    }
 
145
                }
 
146
            }
 
147
          
 
148
          pixels += rowstride;
 
149
        }
 
150
 
 
151
      if (found)
 
152
        {
 
153
          if (out)
 
154
            {
 
155
              out->x = location_relative ? min_x - start_x : min_x;
 
156
              out->y = location_relative ? min_y - start_y : min_y;
 
157
              out->width = max_x - min_x;
 
158
              out->height = max_y - min_y;
 
159
            }
 
160
 
 
161
          return TRUE;
 
162
        }
 
163
      else
 
164
        return FALSE;
 
165
    }
 
166
}
 
167
 
 
168
static GdkPixbuf *
 
169
theme_source_get_pixbuf (ThemeSource    *source,
 
170
                         ThemeLocation  *location,
 
171
                         int             frame_index,
 
172
                         ThemeRectangle *bounds)
 
173
{
 
174
  ThemeImage *image = theme_source_find_image (source, frame_index);
 
175
  GdkPixbuf *result, *tmp_pixbuf;
 
176
  
 
177
  tmp_pixbuf = gdk_pixbuf_new_subpixbuf (image->image,
 
178
                                         bounds->x,
 
179
                                         bounds->y,
 
180
                                         bounds->width,
 
181
                                         bounds->height);
 
182
 
 
183
  result = gdk_pixbuf_copy (tmp_pixbuf);
 
184
  g_object_unref (tmp_pixbuf);
 
185
 
 
186
  return result;
 
187
}
 
188
 
 
189
static gboolean
 
190
theme_source_has_location (ThemeSource   *source,
 
191
                           ThemeLocation *location,
 
192
                           int            frame)
 
193
{
 
194
  return theme_source_location_bounds (source, location, frame, 0, FALSE, NULL);
 
195
}
 
196
 
 
197
static gboolean
 
198
cursor_find_hotspot (ThemeCursor *cursor,
 
199
                     ThemeSource *source,
 
200
                     int         *hot_x,
 
201
                     int         *hot_y)
 
202
{
 
203
  ThemeRectangle bounds;
 
204
  ThemeImage *image;
 
205
 
 
206
  /* Some debugging checks */
 
207
  image = theme_source_find_image (source, THEME_SOURCE_USE_HOTSPOT);
 
208
  if (!image)
 
209
    {
 
210
      g_printerr ("Source at size %d doesn't have a hotspot image\n",
 
211
                  source->size);
 
212
      return FALSE;
 
213
    }
 
214
  
 
215
  if (!gdk_pixbuf_get_has_alpha (image->image) ||
 
216
      gdk_pixbuf_get_colorspace (image->image) != GDK_COLORSPACE_RGB)
 
217
    {
 
218
      g_printerr ("Invalid format for hotspot file\n");
 
219
      return FALSE;
 
220
    }
 
221
  
 
222
  if (theme_source_location_bounds (source, &cursor->location,
 
223
                                    THEME_SOURCE_USE_HOTSPOT,
 
224
                                    0x80, TRUE, &bounds))
 
225
    {
 
226
      if (bounds.width > 1 || bounds.height > 1)
 
227
        {
 
228
          g_printerr ("Multiple hotspots for cursor %s at size %d\n",
 
229
                      cursor->name, source->size);
 
230
          return FALSE;
 
231
        }
 
232
 
 
233
      *hot_x = bounds.x;
 
234
      *hot_y = bounds.y;
 
235
 
 
236
      return TRUE;
 
237
    }
 
238
  else
 
239
    {
 
240
      g_printerr ("Cannot find hotspot for cursor %s at size %d\n",
 
241
                  cursor->name, source->size);
 
242
      return FALSE;
 
243
    }
 
244
 
 
245
  return TRUE;
 
246
}
 
247
 
 
248
static void
 
249
cursor_fetch_pixbuf (ThemeCursor *cursor,
 
250
                     ThemeFrame  *frame,
 
251
                     ThemeSource *source,
 
252
                     int          frame_index)
 
253
{
 
254
  int start_x, start_y;
 
255
  ThemeRectangle bounds_rect;
 
256
  ThemeRectangle hotspot_rect;
 
257
 
 
258
  theme_source_location_start (source, &cursor->location, &start_x, &start_y);
 
259
  theme_source_location_bounds (source, &cursor->location, frame_index,
 
260
                                0, FALSE, &bounds_rect);
 
261
 
 
262
  hotspot_rect.x = start_x + frame->hot_x;
 
263
  hotspot_rect.y = start_y + frame->hot_y;
 
264
  hotspot_rect.width = 1;
 
265
  hotspot_rect.height = 1;
 
266
  theme_rectangle_union (&bounds_rect, &hotspot_rect, &bounds_rect);
 
267
 
 
268
  frame->image = theme_source_get_pixbuf (source, &cursor->location,
 
269
                                          frame_index, &bounds_rect);
 
270
    
 
271
  frame->hot_x -= bounds_rect.x - start_x;
 
272
  frame->hot_y -= bounds_rect.y - start_y;
 
273
}
 
274
 
 
275
static gboolean
 
276
cursor_add_source (ThemeCursor *cursor,
 
277
                   ThemeSource *source)
 
278
{
 
279
  GSList *tmp_list;
 
280
  int hot_x, hot_y;
 
281
  int i;
 
282
 
 
283
  /* If the first frame is missing we silently treat
 
284
   * it as OK
 
285
   */
 
286
  if (!theme_source_has_location (source, &cursor->location, 0))
 
287
    return TRUE;
 
288
  
 
289
  if (!cursor_find_hotspot (cursor, source, &hot_x, &hot_y))
 
290
    return FALSE;
 
291
 
 
292
  for (tmp_list = cursor->frame_configs, i = 0;
 
293
       tmp_list;
 
294
       tmp_list = tmp_list->next, i++)
 
295
    {
 
296
      ThemeFrameConfig *frame_config = tmp_list->data;
 
297
      ThemeFrame *frame;
 
298
 
 
299
      if (i != 0 && !theme_source_has_location (source, &cursor->location, i))
 
300
        {
 
301
          g_printerr ("Frame %d missing for cursor '%s' at size %d\n",
 
302
                      i, cursor->name, source->size);
 
303
          frame = g_new0 (ThemeFrame, 1);
 
304
          frame->size = -1;
 
305
          cursor->frames = g_slist_append (cursor->frames, frame);
 
306
          continue;
 
307
        }
 
308
 
 
309
      frame = g_new0 (ThemeFrame, 1);
 
310
      frame->size = source->size;
 
311
      frame->hot_x = hot_x;
 
312
      frame->hot_y = hot_y;
 
313
      frame->delay = frame_config->delay;
 
314
 
 
315
      cursor_fetch_pixbuf (cursor, frame, source, i);
 
316
 
 
317
      cursor->frames = g_slist_append (cursor->frames, frame);
 
318
    }
 
319
  
 
320
  return TRUE;
 
321
}
 
322
 
 
323
static void
 
324
icon_fetch_pixbuf (ThemeIcon         *icon,
 
325
                   ThemeIconInstance *instance,
 
326
                   ThemeSource       *source)
 
327
{
 
328
  ThemeRectangle bounds_rect;
 
329
 
 
330
  theme_source_location_start (source, &icon->location,
 
331
                               &bounds_rect.x, &bounds_rect.y);
 
332
  bounds_rect.width = source->gridsize;
 
333
  bounds_rect.height = source->gridsize;
 
334
 
 
335
  instance->image = theme_source_get_pixbuf (source, &icon->location,
 
336
                                             0, &bounds_rect);
 
337
}
 
338
 
 
339
static void
 
340
icon_fetch_embedded_rect (ThemeIcon         *icon,
 
341
                          ThemeIconInstance *instance,
 
342
                          ThemeSource       *source)
 
343
{
 
344
  ThemeRectangle bounds_rect;
 
345
  ThemeImage *image;
 
346
 
 
347
  image = theme_source_find_image (source, THEME_SOURCE_USE_EMBEDDED_RECT);
 
348
  if (!image)
 
349
    return;
 
350
 
 
351
  if (!gdk_pixbuf_get_has_alpha (image->image) ||
 
352
      gdk_pixbuf_get_colorspace (image->image) != GDK_COLORSPACE_RGB)
 
353
    {
 
354
      g_printerr ("Invalid format for embedded rectangle file\n");
 
355
      exit (1);
 
356
    }
 
357
  
 
358
  if (theme_source_location_bounds (source, &icon->location,
 
359
                                    THEME_SOURCE_USE_EMBEDDED_RECT,
 
360
                                    0x80, TRUE, &bounds_rect))
 
361
    {
 
362
      instance->embedded_rect = g_memdup (&bounds_rect, sizeof (bounds_rect));
 
363
    }
 
364
}
 
365
 
 
366
static void
 
367
icon_fetch_attach_points (ThemeIcon         *icon,
 
368
                          ThemeIconInstance *instance,
 
369
                          ThemeSource       *source)
 
370
{
 
371
  ThemeImage *image;
 
372
 
 
373
  int start_x, start_y;
 
374
  int i, j;
 
375
  
 
376
  const guchar *pixels;
 
377
  int rowstride;
 
378
 
 
379
  theme_source_location_start (source, &icon->location, &start_x, &start_y);
 
380
  image = theme_source_find_image (source, THEME_SOURCE_USE_ATTACH_POINTS);
 
381
  if (!image)
 
382
    return;
 
383
 
 
384
  if (!gdk_pixbuf_get_has_alpha (image->image) ||
 
385
      gdk_pixbuf_get_colorspace (image->image) != GDK_COLORSPACE_RGB)
 
386
    {
 
387
      g_printerr ("Invalid format for embedded rectangle file\n");
 
388
      exit (1);
 
389
    }
 
390
  
 
391
  rowstride = gdk_pixbuf_get_rowstride (image->image);
 
392
  pixels = gdk_pixbuf_get_pixels (image->image) + start_y * rowstride + start_x * 4;
 
393
  
 
394
  for (j = 0; j < source->gridsize; j++)
 
395
    {
 
396
      for (i = 0; i < source->gridsize; i++)
 
397
        {
 
398
          if (pixels[4*i + 3] > 0x80)
 
399
            {
 
400
              ThemePoint *attach_point = g_new (ThemePoint, 1);
 
401
              attach_point->x = i;
 
402
              attach_point->y = j;
 
403
              
 
404
              instance->attach_points = g_slist_append (instance->attach_points, attach_point);
 
405
            }
 
406
        }
 
407
      
 
408
      pixels += rowstride;
 
409
    }
 
410
}
 
411
 
 
412
static gboolean
 
413
icon_add_source (ThemeIcon   *icon,
 
414
                 ThemeSource *source)
 
415
{
 
416
  ThemeIconInstance *instance;
 
417
 
 
418
  /* If the image is missing, we silently omit it
 
419
   */
 
420
  if (!theme_source_has_location (source, &icon->location, 0))
 
421
    return TRUE;
 
422
 
 
423
  instance = g_new0 (ThemeIconInstance, 1);
 
424
  instance->source = source;
 
425
  
 
426
  icon_fetch_pixbuf (icon, instance, source);
 
427
  icon_fetch_embedded_rect (icon, instance, source);
 
428
  icon_fetch_attach_points (icon, instance, source);
 
429
  
 
430
  icon->instances = g_slist_append (icon->instances, instance);
 
431
  
 
432
  return TRUE;
 
433
}
 
434
 
 
435
static gboolean
 
436
theme_read_cursor (ThemeFile   *theme,
 
437
                   ThemeCursor *cursor)
 
438
{
 
439
  GSList *tmp_list;
 
440
 
 
441
  for (tmp_list = theme->sources; tmp_list; tmp_list = tmp_list->next)
 
442
    {
 
443
      if (!cursor_add_source (cursor, tmp_list->data))
 
444
        return FALSE;
 
445
    }
 
446
 
 
447
  return TRUE;
 
448
}
 
449
 
 
450
static gboolean
 
451
theme_read_icon (ThemeFile *theme,
 
452
                 ThemeIcon *icon)
 
453
{
 
454
  GSList *tmp_list;
 
455
 
 
456
  for (tmp_list = theme->sources; tmp_list; tmp_list = tmp_list->next)
 
457
    {
 
458
      if (!icon_add_source (icon, tmp_list->data))
 
459
        return FALSE;
 
460
    }
 
461
 
 
462
  return TRUE;
 
463
}
 
464
 
 
465
static gboolean
 
466
ensure_directory (const char *directory)
 
467
{
 
468
  if (strchr (directory, '/') != NULL)
 
469
    {
 
470
      char *dirname = g_path_get_dirname (directory);
 
471
      gboolean success = ensure_directory (dirname);
 
472
      g_free (dirname);
 
473
      
 
474
      if (!success)
 
475
        return FALSE;
 
476
    }
 
477
  
 
478
  if (!g_file_test (directory, G_FILE_TEST_IS_DIR) &&
 
479
      mkdir (directory, 0755) < 0)
 
480
    {
 
481
      g_printerr ("Error creating output directory '%s': %s\n",
 
482
                  directory, g_strerror (errno));
 
483
      
 
484
      return FALSE;
 
485
    }
 
486
 
 
487
  return TRUE;
 
488
}
 
489
 
 
490
static void
 
491
theme_write_cursor (ThemeFile   *theme,
 
492
                    ThemeCursor *cursor)
 
493
{
 
494
  GSList *tmp_list;
 
495
  char *curdir;
 
496
  char *config_filename;
 
497
  char *command;
 
498
  FILE *config_file;
 
499
  GError *error = NULL;
 
500
  int status;
 
501
  int i;
 
502
 
 
503
  if (!ensure_directory ("cursors"))
 
504
    return;
 
505
 
 
506
  curdir = g_get_current_dir ();
 
507
  if (chdir ("cursors") < 0)
 
508
    {
 
509
      g_printerr ("Could not change to cursor directory: %s\n", 
 
510
                  g_strerror (errno));
 
511
      return;
 
512
    }
 
513
  
 
514
  if (g_hash_table_lookup (theme->aliases, cursor->name))
 
515
    {
 
516
      g_printerr ("Warning: cursor '%s' overridden by alias\n", cursor->name);
 
517
      goto out;
 
518
    }
 
519
  
 
520
  if (!cursor->frames)
 
521
    goto out;
 
522
  
 
523
  config_filename = g_strconcat (cursor->name, ".cfg", NULL);
 
524
  config_file = fopen (config_filename, "w");
 
525
  
 
526
  if (!config_file)
 
527
    {
 
528
      g_printerr ("Cannot open config file '%s'\n", config_filename);
 
529
      g_free (config_filename);
 
530
      goto out;
 
531
    }
 
532
 
 
533
  for (tmp_list = cursor->frames, i = 0; tmp_list; tmp_list = tmp_list->next, i++)
 
534
    {
 
535
      ThemeFrame *frame = tmp_list->data;
 
536
      char *filename;
 
537
      
 
538
      if (frame->size == -1)
 
539
          continue;
 
540
      filename = g_strdup_printf ("%s-%d.png", cursor->name, i);
 
541
      if (gdk_pixbuf_save (frame->image, filename, "png", &error, NULL))
 
542
        {
 
543
          if (frame->delay > 0)
 
544
            fprintf (config_file, "%d %d %d %s %d\n",
 
545
                     frame->size, frame->hot_x, frame->hot_y, filename, frame->delay);
 
546
          else
 
547
            fprintf (config_file, "%d %d %d %s\n",
 
548
                     frame->size, frame->hot_x, frame->hot_y, filename);
 
549
        }
 
550
      else
 
551
        {
 
552
          g_printerr ("Error saving image file: %s\n", error->message);
 
553
          g_error_free (error);
 
554
        }
 
555
      g_free (filename);
 
556
    }
 
557
 
 
558
  fclose (config_file);
 
559
 
 
560
  command = g_strdup_printf ("sh -c 'xcursorgen %s > %s'\n",
 
561
                             config_filename, cursor->name);
 
562
  if (!g_spawn_command_line_sync (command, NULL, NULL, &status, &error))
 
563
    {
 
564
      g_printerr ("Error running xcursorgen for %s: %s\n",
 
565
                  cursor->name, error->message);
 
566
      g_error_free (error);
 
567
    }
 
568
  else if (status)
 
569
    {
 
570
      g_printerr ("Error running xcursorgen for %s\n",
 
571
                  cursor->name);
 
572
    }
 
573
  else
 
574
    {
 
575
      /* Only delete temporary files if no error occurred
 
576
       */
 
577
      unlink (config_filename);
 
578
      g_free (config_filename);
 
579
      
 
580
      for (tmp_list = cursor->frames, i = 0; tmp_list; tmp_list = tmp_list->next, i++)
 
581
        {
 
582
          char *filename;
 
583
          
 
584
          filename = g_strdup_printf ("%s-%d.png", cursor->name, i);
 
585
          unlink (filename);
 
586
          g_free (filename);
 
587
        }
 
588
    }
 
589
 
 
590
 out:
 
591
  chdir (curdir);
 
592
}
 
593
 
 
594
static char *
 
595
theme_icon_output_dir (ThemeFile   *theme,
 
596
                       ThemeSource *source,
 
597
                       const char  *typedir_override,
 
598
                       const char  *icon_or_alias_name)
 
599
{
 
600
  const char *typedir = typedir_override ? typedir_override : theme->typedir;
 
601
 
 
602
  if (!source->sizedir && !typedir)
 
603
    {
 
604
      g_printerr ("Both typedir and sizedir are empty for '%s'\n",
 
605
                  icon_or_alias_name);
 
606
      return NULL;
 
607
    }
 
608
  
 
609
  if (source->sizedir)
 
610
    return g_build_filename (source->sizedir, typedir, NULL);
 
611
  else
 
612
    return g_strdup (typedir);
 
613
}
 
614
 
 
615
static void
 
616
theme_write_icon_data_file (ThemeFile         *theme,
 
617
                            const char        *output_dir,
 
618
                            const char        *name,
 
619
                            ThemeIconInstance *instance,
 
620
                            GSList            *display_names)
 
621
{
 
622
  char *data_filename;
 
623
  char *data_pathname;
 
624
  FILE *data_file;
 
625
  GSList *tmp_list;
 
626
 
 
627
  data_filename = g_strconcat (name, ".icon", NULL);
 
628
  data_pathname = g_build_filename (output_dir, data_filename, NULL);
 
629
  
 
630
  data_file = fopen (data_pathname, "w");
 
631
  if (!data_file)
 
632
    {
 
633
      g_printerr ("Cannot open icon data file '%s'\n",
 
634
                  data_pathname);
 
635
      exit (1);
 
636
    }
 
637
  
 
638
  fprintf (data_file, "# Generated by icon-slicer, do not edit\n");
 
639
  fprintf (data_file, "[Icon Data]\n");
 
640
  if (instance->embedded_rect)
 
641
    {
 
642
      fprintf (data_file, "EmbeddedTextRectangle=%d,%d,%d,%d\n",
 
643
               instance->embedded_rect->x,
 
644
               instance->embedded_rect->y,
 
645
               instance->embedded_rect->x + instance->embedded_rect->width,
 
646
               instance->embedded_rect->y + instance->embedded_rect->height);
 
647
    }
 
648
  if (instance->attach_points)
 
649
    {
 
650
      GString *attach_point_string = g_string_new (NULL);
 
651
      
 
652
      for (tmp_list = instance->attach_points; tmp_list; tmp_list = tmp_list->next)
 
653
        {
 
654
          ThemePoint *attach_point = tmp_list->data;
 
655
          g_string_append_printf (attach_point_string, "%d,%d",
 
656
                                  attach_point->x, attach_point->y);
 
657
          if (tmp_list->next)
 
658
            g_string_append (attach_point_string, "|");
 
659
        }
 
660
      
 
661
      fprintf (data_file, "AttachPoints=%s\n",
 
662
               attach_point_string->str);
 
663
      
 
664
      g_string_free (attach_point_string, TRUE);
 
665
    }
 
666
  for (tmp_list = display_names; tmp_list; tmp_list = tmp_list->next)
 
667
    {
 
668
      ThemeDisplayName *display_name = tmp_list->data;
 
669
      
 
670
      if (display_name->lang)
 
671
        fprintf (data_file, "DisplayName[%s]=%s\n",
 
672
                 display_name->lang, display_name->str);
 
673
      else
 
674
        fprintf (data_file, "DisplayName=%s\n",
 
675
                 display_name->str);
 
676
    }
 
677
  
 
678
  fclose (data_file);
 
679
  
 
680
  g_free (data_filename);
 
681
  g_free (data_pathname);
 
682
}
 
683
 
 
684
static void
 
685
theme_write_icon (ThemeFile *theme,
 
686
                  ThemeIcon *icon)
 
687
{
 
688
  GSList *tmp_list;
 
689
  GError *error = NULL;
 
690
  int i;
 
691
 
 
692
  if (g_hash_table_lookup (theme->aliases, icon->name))
 
693
    {
 
694
      g_printerr ("Warning: icon '%s' overridden by alias\n", icon->name);
 
695
      return;
 
696
    }
 
697
  
 
698
  if (!icon->instances)
 
699
    return;
 
700
  
 
701
  for (tmp_list = icon->instances, i = 0; tmp_list; tmp_list = tmp_list->next, i++)
 
702
    {
 
703
      ThemeIconInstance *instance = tmp_list->data;
 
704
      char *output_dir, *filename, *pathname;
 
705
 
 
706
      output_dir = theme_icon_output_dir (theme, instance->source,
 
707
                                          icon->typedir, icon->name);
 
708
      if (!output_dir)
 
709
        exit (1);
 
710
 
 
711
      if (!ensure_directory (output_dir))
 
712
        exit (1);
 
713
      
 
714
      filename = g_strconcat (icon->name, ".png", NULL);
 
715
      pathname = g_build_filename (output_dir, filename, NULL);
 
716
      if (!gdk_pixbuf_save (instance->image, pathname, "png", &error, NULL))
 
717
        {
 
718
          g_printerr ("Error saving image file: %s\n", error->message);
 
719
          g_error_free (error);
 
720
          exit (1);
 
721
        }
 
722
      g_free (filename);
 
723
      g_free (pathname);
 
724
 
 
725
      if (instance->embedded_rect || instance->attach_points || icon->display_names)
 
726
        {
 
727
          theme_write_icon_data_file (theme, output_dir, icon->name,
 
728
                                      instance, icon->display_names);
 
729
        }
 
730
      
 
731
      g_free (output_dir);
 
732
    }
 
733
}
 
734
 
 
735
static const char *
 
736
theme_check_alias (ThemeFile      *theme,
 
737
                   ThemeAlias     *alias,
 
738
                   ThemeAliasType *type,
 
739
                   const char    **target_typedir)
 
740
{
 
741
  ThemeAlias *tortoise = alias;
 
742
  ThemeAlias *hare = alias;
 
743
  ThemeCursor *cursor_target;
 
744
  ThemeIcon *icon_target;
 
745
 
 
746
  /* Dereference, using tortoise-and-hare checking for circular aliases
 
747
   */
 
748
  while (TRUE)
 
749
    {
 
750
      ThemeAlias *next;
 
751
 
 
752
      next = g_hash_table_lookup (theme->aliases, hare->target);
 
753
      if (!next)
 
754
        break;
 
755
      hare = next;
 
756
 
 
757
      if (hare == tortoise)
 
758
        goto found_loop;
 
759
      
 
760
      next = g_hash_table_lookup (theme->aliases, hare->target);
 
761
      if (!next)
 
762
        break;
 
763
      hare = next;
 
764
 
 
765
      if (hare == tortoise)
 
766
        goto found_loop;
 
767
      
 
768
      tortoise = g_hash_table_lookup (theme->aliases, tortoise->target);
 
769
    }
 
770
 
 
771
  /* Now check that the actual target exists and is sensible.
 
772
   */
 
773
  cursor_target = g_hash_table_lookup (theme->cursors, hare->target);
 
774
  icon_target = g_hash_table_lookup (theme->icons, hare->target);
 
775
 
 
776
  if (cursor_target && icon_target)
 
777
    {
 
778
      g_printerr ("Alias '%s' points to '%s', which is both a cursor and an icon\n",
 
779
                  alias->name, hare->target);
 
780
      return NULL;
 
781
    }
 
782
 
 
783
  if (!cursor_target && !icon_target)
 
784
    {
 
785
      g_printerr ("Alias '%s' points to '%s', which is not in the theme\n",
 
786
                  alias->name, hare->target);
 
787
      return NULL;
 
788
    }
 
789
 
 
790
  if (cursor_target)
 
791
    {
 
792
      if (!cursor_target->frames)
 
793
        {
 
794
          g_printerr ("Alias '%s' points to cursor '%s', which is not present in any source\n",
 
795
                      alias->name, hare->target);
 
796
          return NULL;
 
797
        }
 
798
          
 
799
      *type = THEME_ALIAS_CURSOR;
 
800
      *target_typedir = NULL;
 
801
    }
 
802
 
 
803
  if (icon_target)
 
804
    {
 
805
      if (!icon_target->instances)
 
806
        {
 
807
          g_printerr ("Alias '%s' points to icon '%s', which is not present in any source\n",
 
808
                      alias->name, hare->target);
 
809
          return NULL;
 
810
        }
 
811
          
 
812
      *type = THEME_ALIAS_ICON;
 
813
      *target_typedir = icon_target->typedir;
 
814
    }
 
815
 
 
816
  return hare->target;
 
817
 
 
818
 found_loop:
 
819
  g_printerr ("Circular looop detected when dereferencing alias '%s'\n",
 
820
              alias->name);
 
821
  return NULL;
 
822
}
 
823
 
 
824
static char *
 
825
relative_path (const char *directory,
 
826
               const char *base)
 
827
{
 
828
  char **dir_comps = g_strsplit (directory, "/", -1);
 
829
  char **base_comps = g_strsplit (base, "/", -1);
 
830
  char **dirp, **basep;
 
831
  GString *result = g_string_new (NULL);
 
832
 
 
833
  dirp = dir_comps;
 
834
  basep = base_comps;
 
835
 
 
836
  while (*dirp && *basep && strcmp (*dirp, *basep) == 0)
 
837
    {
 
838
      dirp++;
 
839
      basep++;
 
840
    }
 
841
 
 
842
  while (*basep)
 
843
    {
 
844
      if (result->len)
 
845
        g_string_append_c (result, '/');
 
846
 
 
847
      g_string_append (result, "..");
 
848
      basep++;
 
849
    }
 
850
 
 
851
  while (*dirp)
 
852
    {
 
853
      if (result->len)
 
854
        g_string_append_c (result, '/');
 
855
 
 
856
      g_string_append (result, *dirp);
 
857
      dirp++;
 
858
    }
 
859
 
 
860
  g_strfreev (dir_comps);
 
861
  g_strfreev (base_comps);
 
862
 
 
863
  if (result->len)
 
864
    return g_string_free (result, FALSE);
 
865
  else
 
866
    {
 
867
      g_string_free (result, TRUE);
 
868
      return NULL;
 
869
    }
 
870
}
 
871
 
 
872
static gboolean
 
873
make_symlink (const char *directory,
 
874
              const char *alias,
 
875
              const char *target_directory,
 
876
              const char *target)
 
877
{
 
878
  char *filename;
 
879
  char *target_filename;
 
880
  gboolean result = FALSE;
 
881
  char *relative_target_directory;
 
882
 
 
883
  if (!ensure_directory (directory))
 
884
    return FALSE;
 
885
 
 
886
  relative_target_directory = relative_path (target_directory, directory);
 
887
  
 
888
  filename = g_build_filename (directory, alias, NULL);
 
889
  if (relative_target_directory)
 
890
    target_filename = g_build_filename (relative_target_directory, target, NULL);
 
891
  else
 
892
    target_filename = g_strdup (target);
 
893
 
 
894
  unlink (filename);
 
895
  if (symlink (target_filename, filename) < 0)
 
896
    g_printerr ("Error creating alias symlink from '%s' to '%s': %s\n",
 
897
                filename, target_filename, g_strerror (errno));
 
898
  else
 
899
    result = TRUE;
 
900
      
 
901
  g_free (filename);
 
902
  g_free (relative_target_directory);
 
903
  g_free (target_filename);
 
904
 
 
905
  return result;
 
906
}
 
907
 
 
908
static void
 
909
theme_write_alias (ThemeFile  *theme,
 
910
                   ThemeAlias *alias)
 
911
{
 
912
  ThemeAliasType type;
 
913
  const char *target_typedir;
 
914
  const char *target = theme_check_alias (theme, alias, &type, &target_typedir);
 
915
  if (!target)
 
916
    exit (1);
 
917
 
 
918
  if (type == THEME_ALIAS_CURSOR)
 
919
    {
 
920
      if (!make_symlink ("cursors", alias->name, "cursors", target))
 
921
        exit (1);
 
922
    }
 
923
  else
 
924
    {
 
925
      ThemeIcon *icon = g_hash_table_lookup (theme->icons, target);
 
926
      GSList *tmp_list;
 
927
      char *pngalias = g_strdup_printf ("%s.png", alias->name);
 
928
      char *pngtarget = g_strdup_printf ("%s.png", target);
 
929
      char *dataalias = g_strdup_printf ("%s.icon", alias->name);
 
930
      char *datatarget = g_strdup_printf ("%s.icon", target);
 
931
 
 
932
      for (tmp_list = icon->instances; tmp_list; tmp_list = tmp_list->next)
 
933
        {
 
934
          ThemeIconInstance *instance = tmp_list->data;
 
935
          char *output_dir;
 
936
          char *target_output_dir;
 
937
          
 
938
          output_dir = theme_icon_output_dir (theme, instance->source,
 
939
                                              alias->typedir, alias->name);
 
940
          if (!output_dir)
 
941
            exit (1);
 
942
 
 
943
          target_output_dir = theme_icon_output_dir (theme, instance->source,
 
944
                                                     target_typedir, target);
 
945
          if (!output_dir)
 
946
            exit (1);
 
947
 
 
948
          if (!ensure_directory (output_dir))
 
949
            exit (1);
 
950
 
 
951
          if (!make_symlink (output_dir, pngalias,
 
952
                             target_output_dir, pngtarget))
 
953
            exit (1);
 
954
 
 
955
          if (alias->display_names)
 
956
            theme_write_icon_data_file (theme, output_dir, alias->name,
 
957
                                        instance, alias->display_names);
 
958
          else if ((instance->embedded_rect || instance->attach_points || icon->display_names))
 
959
            {
 
960
              if (!make_symlink (output_dir, dataalias,
 
961
                                 target_output_dir, datatarget))
 
962
                exit (1);
 
963
            }
 
964
 
 
965
          g_free (output_dir);
 
966
          g_free (target_output_dir);
 
967
        }
 
968
 
 
969
      g_free (pngalias);
 
970
      g_free (pngtarget);
 
971
      g_free (dataalias);
 
972
      g_free (datatarget);
 
973
    }
 
974
}
 
975
 
 
976
static void
 
977
write_cursor_foreach (gpointer key,
 
978
                      gpointer value,
 
979
                      gpointer data)
 
980
{
 
981
  theme_write_cursor (data, value);
 
982
}
 
983
 
 
984
static void
 
985
write_icon_foreach (gpointer key,
 
986
                    gpointer value,
 
987
                    gpointer data)
 
988
{
 
989
  theme_write_icon (data, value);
 
990
}
 
991
 
 
992
static void
 
993
write_alias_foreach (gpointer key,
 
994
                     gpointer value,
 
995
                     gpointer data)
 
996
{
 
997
  theme_write_alias (data, value);
 
998
}
 
999
 
 
1000
static gboolean
 
1001
theme_write (ThemeFile  *theme,
 
1002
             const char *output_dir)
 
1003
{
 
1004
  char *curdir;
 
1005
  
 
1006
  if (!g_file_test (output_dir, G_FILE_TEST_IS_DIR))
 
1007
    {
 
1008
      g_printerr ("Output directory '%s' does not exist\n", output_dir);
 
1009
      return FALSE;
 
1010
    }
 
1011
 
 
1012
  curdir = g_get_current_dir ();
 
1013
  if (chdir (output_dir) < 0)
 
1014
    {
 
1015
      g_printerr ("Could not change to output directory '%s'\n", output_dir);
 
1016
      return FALSE;
 
1017
    }
 
1018
 
 
1019
  g_hash_table_foreach (theme->cursors,
 
1020
                        write_cursor_foreach,
 
1021
                        theme);
 
1022
 
 
1023
  g_hash_table_foreach (theme->icons,
 
1024
                        write_icon_foreach,
 
1025
                        theme);
 
1026
 
 
1027
  g_hash_table_foreach (theme->aliases,
 
1028
                        write_alias_foreach,
 
1029
                        theme);
 
1030
 
 
1031
  chdir (curdir);
 
1032
 
 
1033
  return TRUE;
 
1034
}
 
1035
 
 
1036
void
 
1037
usage (void)
 
1038
{
 
1039
  g_printerr ("Usage: cursorthemegen CONFIG_FILE OUTPUT_DIR\n");
 
1040
  exit (1);
 
1041
}
 
1042
 
 
1043
static void
 
1044
read_cursor_foreach (gpointer key,
 
1045
                     gpointer value,
 
1046
                     gpointer data)
 
1047
{
 
1048
  if (!theme_read_cursor (data, value))
 
1049
    exit (1);
 
1050
}
 
1051
 
 
1052
static void
 
1053
read_icon_foreach (gpointer key,
 
1054
                   gpointer value,
 
1055
                   gpointer data)
 
1056
{
 
1057
  if (!theme_read_icon (data, value))
 
1058
    exit (1);
 
1059
}
 
1060
 
 
1061
static int want_my_version = FALSE;
 
1062
static const char *output_dir = NULL;
 
1063
static const char *image_dir = NULL;
 
1064
  
 
1065
static const struct poptOption options_table[] = {
 
1066
  { "version", 0, POPT_ARG_NONE, &want_my_version, 0,
 
1067
    "output version of icon-slicer" },
 
1068
    { "output-dir", 0, POPT_ARG_STRING, &output_dir, 0,
 
1069
    "directory into which to write output", "DIRECTORY" },
 
1070
  { "image-dir", 0, POPT_ARG_STRING, &image_dir, 0,
 
1071
    "directory in which to find source images", "DIRECTORY" },
 
1072
  POPT_AUTOHELP
 
1073
  { NULL, 0, 0, NULL, 0 }
 
1074
};
 
1075
 
 
1076
int
 
1077
main (int argc, char **argv)
 
1078
{
 
1079
  poptContext opt_context;
 
1080
  const char *arg;
 
1081
  int result;
 
1082
 
 
1083
  g_type_init ();
 
1084
  
 
1085
  opt_context = poptGetContext ("xsri", argc, (const char **)argv,
 
1086
                                options_table, 0);
 
1087
 
 
1088
  result = poptGetNextOpt (opt_context);
 
1089
  if (result != -1)
 
1090
    {
 
1091
      g_printerr ("xsri: %s: %s\n",
 
1092
              poptBadOption(opt_context, POPT_BADOPTION_NOALIAS),
 
1093
                  poptStrerror(result));
 
1094
      return 1;
 
1095
    }
 
1096
 
 
1097
  if (want_my_version)
 
1098
    {
 
1099
      printf ("icon-slicer version %s\n", PACKAGE_VERSION);
 
1100
      return 0;
 
1101
    }
 
1102
 
 
1103
  if (!output_dir)
 
1104
    {
 
1105
      g_printerr ("Output directory must be given with --output-dir\n");
 
1106
      poptPrintUsage (opt_context, stderr, 0);
 
1107
      return 1;
 
1108
    }
 
1109
  
 
1110
  arg = poptGetArg (opt_context);
 
1111
  if (!arg)
 
1112
    {
 
1113
      poptPrintUsage (opt_context, stderr, 0);
 
1114
      return 1;
 
1115
    }
 
1116
 
 
1117
  while (arg)
 
1118
    {
 
1119
      ThemeFile *theme;
 
1120
      
 
1121
      theme = theme_file_read (arg, image_dir);
 
1122
      if (!theme)
 
1123
        return 1;
 
1124
      
 
1125
      g_hash_table_foreach (theme->cursors,
 
1126
                            read_cursor_foreach,
 
1127
                            theme);
 
1128
      
 
1129
      g_hash_table_foreach (theme->icons,
 
1130
                            read_icon_foreach,
 
1131
                            theme);
 
1132
      
 
1133
      if (!theme_write (theme, output_dir))
 
1134
        return 1;
 
1135
 
 
1136
      theme_file_free (theme);
 
1137
      
 
1138
      arg = poptGetArg (opt_context);
 
1139
    }
 
1140
  
 
1141
  return 0;
 
1142
}