image_list.c 19.5 KB
Newer Older
1
/*
2
 *  Copyright (c) Stephan Arts 2009-2011 <stephan@xfce.org>
3 4 5 6 7
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
Stephan Arts's avatar
Stephan Arts committed
8 9
 *
 *  This program is distributed in the hope that it will be useful,
10 11 12 13 14 15 16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Stephan Arts's avatar
Stephan Arts committed
17 18
 *
 *  Sorting-algorithm taken from the thunar filemanager.
19 20 21 22 23 24 25
 */

#include <config.h>

#include <gtk/gtk.h>
#include <gtk/gtkmarshal.h>

26
#include <stdlib.h>
27 28 29 30
#include <string.h>

#include <libexif/exif-data.h>

Stephan Arts's avatar
Stephan Arts committed
31
#include "file.h"
32
#include "image_list.h"
33
#include "settings.h"
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

static void 
rstto_image_list_init(RsttoImageList *);
static void
rstto_image_list_class_init(RsttoImageListClass *);
static void
rstto_image_list_dispose(GObject *object);

static void 
rstto_image_list_iter_init(RsttoImageListIter *);
static void
rstto_image_list_iter_class_init(RsttoImageListIterClass *);
static void
rstto_image_list_iter_dispose(GObject *object);

static RsttoImageListIter * rstto_image_list_iter_new ();

static gint
Stephan Arts's avatar
Stephan Arts committed
52
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b);
Stephan Arts's avatar
Stephan Arts committed
53
static gint
Stephan Arts's avatar
Stephan Arts committed
54
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b);
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

static GObjectClass *parent_class = NULL;
static GObjectClass *iter_parent_class = NULL;

enum
{
    RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE = 0,
    RSTTO_IMAGE_LIST_SIGNAL_REMOVE_IMAGE,
    RSTTO_IMAGE_LIST_SIGNAL_REMOVE_ALL,
    RSTTO_IMAGE_LIST_SIGNAL_COUNT
};

enum
{
    RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED = 0,
70
    RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE,
71 72 73 74 75 76
    RSTTO_IMAGE_LIST_ITER_SIGNAL_COUNT
};

struct _RsttoImageListIterPriv
{
    RsttoImageList *image_list;
Stephan Arts's avatar
Stephan Arts committed
77
    RsttoFile *file;
78 79 80 81 82 83
};

struct _RsttoImageListPriv
{
    GList *images;
    gint n_images;
Stephan Arts's avatar
Stephan Arts committed
84 85

    GSList *iterators;
Stephan Arts's avatar
Stephan Arts committed
86
    GCompareFunc cb_rstto_image_list_compare_func;
87 88 89 90 91 92
};

static gint rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_COUNT];
static gint rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_COUNT];

GType
Stephan Arts's avatar
Stephan Arts committed
93
rstto_image_list_get_type (void)
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
{
    static GType rstto_image_list_type = 0;

    if (!rstto_image_list_type)
    {
        static const GTypeInfo rstto_image_list_info = 
        {
            sizeof (RsttoImageListClass),
            (GBaseInitFunc) NULL,
            (GBaseFinalizeFunc) NULL,
            (GClassInitFunc) rstto_image_list_class_init,
            (GClassFinalizeFunc) NULL,
            NULL,
            sizeof (RsttoImageList),
            0,
            (GInstanceInitFunc) rstto_image_list_init,
            NULL
        };

        rstto_image_list_type = g_type_register_static (G_TYPE_OBJECT, "RsttoImageList", &rstto_image_list_info, 0);
    }
    return rstto_image_list_type;
}

static void
rstto_image_list_init(RsttoImageList *image_list)
{
121

122
    image_list->priv = g_new0 (RsttoImageListPriv, 1);
Stephan Arts's avatar
Stephan Arts committed
123
    image_list->priv->cb_rstto_image_list_compare_func = (GCompareFunc)cb_rstto_image_list_image_name_compare_func;
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
}

static void
rstto_image_list_class_init(RsttoImageListClass *nav_class)
{
    GObjectClass *object_class = G_OBJECT_CLASS(nav_class);

    parent_class = g_type_class_peek_parent(nav_class);

    object_class->dispose = rstto_image_list_dispose;

    rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE] = g_signal_new("new-image",
            G_TYPE_FROM_CLASS(nav_class),
            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE,
            1,
            G_TYPE_OBJECT,
            NULL);

    rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_IMAGE] = g_signal_new("remove-image",
            G_TYPE_FROM_CLASS(nav_class),
            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE,
            1,
            G_TYPE_OBJECT,
            NULL);

    rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_ALL] = g_signal_new("remove-all",
            G_TYPE_FROM_CLASS(nav_class),
            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE,
            0,
            NULL);
}

static void
rstto_image_list_dispose(GObject *object)
{
    /*RsttoImageList *image_list = RSTTO_IMAGE_LIST(object);*/
}

RsttoImageList *
Stephan Arts's avatar
Stephan Arts committed
178
rstto_image_list_new (void)
179 180 181 182 183 184 185 186 187
{
    RsttoImageList *image_list;

    image_list = g_object_new(RSTTO_TYPE_IMAGE_LIST, NULL);

    return image_list;
}

gboolean
Stephan Arts's avatar
Stephan Arts committed
188
rstto_image_list_add_file (RsttoImageList *image_list, RsttoFile *file, GError **error)
189
{
Stephan Arts's avatar
Stephan Arts committed
190
    GList *image_iter = g_list_find (image_list->priv->images, file);
191

Stephan Arts's avatar
Stephan Arts committed
192 193
    if (!image_iter)
    {
194
        if (file)
Stephan Arts's avatar
Stephan Arts committed
195
        {
196 197
            image_list->priv->images = g_list_insert_sorted (image_list->priv->images, file, rstto_image_list_get_compare_func (image_list));

Stephan Arts's avatar
Stephan Arts committed
198 199
            image_list->priv->n_images++;

200
            g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE], 0, file, NULL);
Stephan Arts's avatar
Stephan Arts committed
201
            if (image_list->priv->n_images == 1)
Stephan Arts's avatar
Stephan Arts committed
202
            {
Stephan Arts's avatar
Stephan Arts committed
203 204 205 206 207 208 209
                /** TODO: update all iterators */
                GSList *iter = image_list->priv->iterators;
                while (iter)
                {
                    g_signal_emit (G_OBJECT (iter->data), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
                    iter = g_slist_next (iter);
                }
Stephan Arts's avatar
Stephan Arts committed
210
            }
Stephan Arts's avatar
Stephan Arts committed
211
            return TRUE;
Stephan Arts's avatar
Stephan Arts committed
212
        }
Stephan Arts's avatar
Stephan Arts committed
213
        return FALSE;
214
    }
Stephan Arts's avatar
Stephan Arts committed
215 216 217
    g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE], 0, image_iter->data, NULL);

    return TRUE;
218 219 220 221 222 223 224 225
}

gint
rstto_image_list_get_n_images (RsttoImageList *image_list)
{
    return g_list_length (image_list->priv->images);
}

Stephan Arts's avatar
Stephan Arts committed
226 227 228 229 230 231 232 233
/**
 * rstto_image_list_get_iter:
 * @image_list:
 *
 * TODO: track iterators
 *
 * return iter;
 */
234 235 236
RsttoImageListIter *
rstto_image_list_get_iter (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
237
    RsttoFile *file = NULL;
Stephan Arts's avatar
Stephan Arts committed
238
    RsttoImageListIter *iter = NULL;
239
    if (image_list->priv->images)
240
        file = image_list->priv->images->data;
241

242
    iter = rstto_image_list_iter_new (image_list, file);
243

Stephan Arts's avatar
Stephan Arts committed
244 245
    image_list->priv->iterators = g_slist_prepend (image_list->priv->iterators, iter);

246 247 248 249 250
    return iter;
}


void
Stephan Arts's avatar
Stephan Arts committed
251
rstto_image_list_remove_file (RsttoImageList *image_list, RsttoFile *file)
252
{
Stephan Arts's avatar
Stephan Arts committed
253
    GSList *iter = NULL;
Stephan Arts's avatar
Stephan Arts committed
254
    RsttoFile *afile = NULL;
Stephan Arts's avatar
Stephan Arts committed
255

256
    if (g_list_find(image_list->priv->images, file))
257
    {
Stephan Arts's avatar
Stephan Arts committed
258

Stephan Arts's avatar
Stephan Arts committed
259
        iter = image_list->priv->iterators;
Stephan Arts's avatar
Stephan Arts committed
260 261
        while (iter)
        {
Stephan Arts's avatar
Stephan Arts committed
262
            if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
Stephan Arts's avatar
Stephan Arts committed
263
            {
264
                if (rstto_image_list_iter_get_position (iter->data) == rstto_image_list_get_n_images (image_list)-1)
265
                {
266
                    rstto_image_list_iter_previous (iter->data);
267 268 269
                }
                else
                {
270
                    rstto_image_list_iter_next (iter->data);
271
                }
272 273 274 275
                /* If the image is still the same, 
                 * it's a single item list,
                 * and we should force the image in this iter to NULL
                 */
Stephan Arts's avatar
Stephan Arts committed
276
                if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
277
                {
278
                    ((RsttoImageListIter *)(iter->data))->priv->file = NULL;
279
                    g_signal_emit (G_OBJECT (iter->data), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
280
                }
Stephan Arts's avatar
Stephan Arts committed
281 282 283
            }
            iter = g_slist_next (iter);
        }
284

285
        image_list->priv->images = g_list_remove (image_list->priv->images, file);
286 287 288
        iter = image_list->priv->iterators;
        while (iter)
        {
289 290
            afile = rstto_image_list_iter_get_file(iter->data);
            if (NULL != afile)
291
            {
Stephan Arts's avatar
Stephan Arts committed
292
                if (rstto_file_equal(afile, file))
293
                {
294
                    rstto_image_list_iter_next (iter->data);
295
                }
296 297 298 299
            }
            iter = g_slist_next (iter);
        }

300 301
        g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_IMAGE], 0, file, NULL);
        g_object_unref(file);
302 303 304 305 306 307
    }
}

void
rstto_image_list_remove_all (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
308
    GSList *iter = NULL;
309 310 311
    g_list_foreach (image_list->priv->images, (GFunc)g_object_unref, NULL);
    g_list_free (image_list->priv->images);
    image_list->priv->images = NULL;
Stephan Arts's avatar
Stephan Arts committed
312

Stephan Arts's avatar
Stephan Arts committed
313
    iter = image_list->priv->iterators;
Stephan Arts's avatar
Stephan Arts committed
314 315
    while (iter)
    {
Stephan Arts's avatar
Stephan Arts committed
316
        rstto_image_list_iter_set_position (iter->data, -1);
Stephan Arts's avatar
Stephan Arts committed
317 318 319
        iter = g_slist_next (iter);
    }
    g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_ALL], 0, NULL);
320 321 322 323 324
}



GType
Stephan Arts's avatar
Stephan Arts committed
325
rstto_image_list_iter_get_type (void)
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
{
    static GType rstto_image_list_iter_type = 0;

    if (!rstto_image_list_iter_type)
    {
        static const GTypeInfo rstto_image_list_iter_info = 
        {
            sizeof (RsttoImageListIterClass),
            (GBaseInitFunc) NULL,
            (GBaseFinalizeFunc) NULL,
            (GClassInitFunc) rstto_image_list_iter_class_init,
            (GClassFinalizeFunc) NULL,
            NULL,
            sizeof (RsttoImageListIter),
            0,
            (GInstanceInitFunc) rstto_image_list_iter_init,
            NULL
        };

        rstto_image_list_iter_type = g_type_register_static (G_TYPE_OBJECT, "RsttoImageListIter", &rstto_image_list_iter_info, 0);
    }
    return rstto_image_list_iter_type;
}

static void
rstto_image_list_iter_init (RsttoImageListIter *iter)
{
    iter->priv = g_new0 (RsttoImageListIterPriv, 1);
}

static void
rstto_image_list_iter_class_init(RsttoImageListIterClass *iter_class)
{
    GObjectClass *object_class = G_OBJECT_CLASS(iter_class);

    iter_parent_class = g_type_class_peek_parent(iter_class);

    object_class->dispose = rstto_image_list_iter_dispose;

365 366 367 368 369 370 371 372 373 374 375
    rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE] = g_signal_new("prepare-change",
            G_TYPE_FROM_CLASS(iter_class),
            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE,
            0,
            NULL);

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
    rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED] = g_signal_new("changed",
            G_TYPE_FROM_CLASS(iter_class),
            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE,
            0,
            NULL);

}

static void
rstto_image_list_iter_dispose (GObject *object)
{
    RsttoImageListIter *iter = RSTTO_IMAGE_LIST_ITER(object);
393
    if (iter->priv->file)
394
    {
395
        iter->priv->file = NULL;
396
    }
Stephan Arts's avatar
Stephan Arts committed
397 398 399 400 401 402

    if (iter->priv->image_list)
    {
        iter->priv->image_list->priv->iterators = g_slist_remove (iter->priv->image_list->priv->iterators, iter);
        iter->priv->image_list= NULL;
    }
403 404 405
}

static RsttoImageListIter *
Stephan Arts's avatar
Stephan Arts committed
406
rstto_image_list_iter_new (RsttoImageList *nav, RsttoFile *file)
407 408 409 410
{
    RsttoImageListIter *iter;

    iter = g_object_new(RSTTO_TYPE_IMAGE_LIST_ITER, NULL);
411
    iter->priv->file = file;
412 413 414 415 416 417
    iter->priv->image_list = nav;

    return iter;
}

gboolean
Stephan Arts's avatar
Stephan Arts committed
418
rstto_image_list_iter_find_file (RsttoImageListIter *iter, RsttoFile *file)
419
{
420
    gint pos = g_list_index (iter->priv->image_list->priv->images, file);
421 422
    if (pos > -1)
    {
423 424
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

425
        if (iter->priv->file)
426
        {
427
            iter->priv->file = NULL;
428
        }
429
        iter->priv->file = file;
430 431 432 433 434 435 436 437 438 439 440

        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);

        return TRUE;
    }
    return FALSE;
}

gint
rstto_image_list_iter_get_position (RsttoImageListIter *iter)
{
441
    if ( NULL == iter->priv->file )
442
    {
443
        return -1;
444
    }
445
    return g_list_index (iter->priv->image_list->priv->images, iter->priv->file);
446 447
}

Stephan Arts's avatar
Stephan Arts committed
448
RsttoFile *
449
rstto_image_list_iter_get_file (RsttoImageListIter *iter)
450
{
451
    return iter->priv->file;
452 453 454
}


Stephan Arts's avatar
Stephan Arts committed
455
void
456 457
rstto_image_list_iter_set_position (RsttoImageListIter *iter, gint pos)
{
458 459
    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

460
    if (iter->priv->file)
461
    {
462
        iter->priv->file = NULL;
463 464
    }

Stephan Arts's avatar
Stephan Arts committed
465
    if (pos >= 0)
Stephan Arts's avatar
Stephan Arts committed
466
    {
467
        iter->priv->file = g_list_nth_data (iter->priv->image_list->priv->images, pos); 
Stephan Arts's avatar
Stephan Arts committed
468
    }
469

470 471 472
    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
}

Stephan Arts's avatar
Stephan Arts committed
473
void
474 475
rstto_image_list_iter_next (RsttoImageListIter *iter)
{
476
    GList *position = NULL;
Stephan Arts's avatar
Stephan Arts committed
477
    RsttoSettings *settings = NULL;
478
    RsttoFile *file = iter->priv->file;
479 480 481

    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

482
    if (iter->priv->file)
483
    {
484 485
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
486 487
    }

488 489
    position = g_list_next (position);
    if (position)
490
        iter->priv->file = position->data; 
491 492
    else
    {
Stephan Arts's avatar
Stephan Arts committed
493 494 495
        settings = rstto_settings_new();

        if (rstto_settings_get_boolean_property (settings, "wrap-images"))
496 497 498 499
            position = g_list_first (iter->priv->image_list->priv->images);
        else
            position = g_list_last (iter->priv->image_list->priv->images);

500
        if (position)
501
            iter->priv->file = position->data; 
502
        else
503
            iter->priv->file = NULL;
Stephan Arts's avatar
Stephan Arts committed
504 505

        g_object_unref (settings);
506 507
    }

508 509 510 511
    if (file != iter->priv->file)
    {
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
    }
512 513
}

Stephan Arts's avatar
Stephan Arts committed
514
void
515 516
rstto_image_list_iter_previous (RsttoImageListIter *iter)
{
517
    GList *position = NULL;
Stephan Arts's avatar
Stephan Arts committed
518
    RsttoSettings *settings = NULL;
519
    RsttoFile *file = iter->priv->file;
520 521 522

    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

523
    if (iter->priv->file)
524
    {
525 526
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
527 528
    }

529 530
    position = g_list_previous (position);
    if (position)
531
    {
532
        iter->priv->file = position->data; 
533
    }
534 535
    else
    {
Stephan Arts's avatar
Stephan Arts committed
536 537 538
        settings = rstto_settings_new();

        if (rstto_settings_get_boolean_property (settings, "wrap-images"))
539 540 541 542
            position = g_list_last (iter->priv->image_list->priv->images);
        else
            position = g_list_first (iter->priv->image_list->priv->images);

543
        if (position)
544
            iter->priv->file = position->data; 
545
        else
546
            iter->priv->file = NULL;
Stephan Arts's avatar
Stephan Arts committed
547 548

        g_object_unref (settings);
549
    }
550

551 552 553 554
    if (file != iter->priv->file)
    {
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
    }
555 556 557 558 559
}

RsttoImageListIter *
rstto_image_list_iter_clone (RsttoImageListIter *iter)
{
560
    RsttoImageListIter *new_iter = rstto_image_list_iter_new (iter->priv->image_list, iter->priv->file);
Stephan Arts's avatar
Stephan Arts committed
561
    rstto_image_list_iter_set_position (new_iter, rstto_image_list_iter_get_position(iter));
562 563 564

    return new_iter;
}
565 566 567 568

GCompareFunc
rstto_image_list_get_compare_func (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
569 570 571
    return (GCompareFunc)image_list->priv->cb_rstto_image_list_compare_func;
}

572 573 574
void
rstto_image_list_set_compare_func (RsttoImageList *image_list, GCompareFunc func)
{
575
    GSList *iter = NULL;
576
    image_list->priv->cb_rstto_image_list_compare_func = func;
577
    image_list->priv->images = g_list_sort (image_list->priv->images,  func);
578

579 580 581 582
    for (iter = image_list->priv->iterators; iter != NULL; iter = g_slist_next (iter))
    {
        g_signal_emit (G_OBJECT (iter->data), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
    }
583 584
}

Stephan Arts's avatar
Stephan Arts committed
585 586 587 588
/***********************/
/*  Compare Functions  */
/***********************/

589 590 591
void
rstto_image_list_set_sort_by_name (RsttoImageList *image_list)
{
592
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_image_name_compare_func);
593 594 595 596 597
}

void
rstto_image_list_set_sort_by_date (RsttoImageList *image_list)
{
598
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_exif_date_compare_func);
599 600
}

Stephan Arts's avatar
Stephan Arts committed
601 602 603 604 605 606 607 608 609
/**
 * cb_rstto_image_list_image_name_compare_func:
 * @a:
 * @b:
 *
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
610
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
611
{
Stephan Arts's avatar
Stephan Arts committed
612 613
    const gchar *a_base = rstto_file_get_display_name (a);
    const gchar *b_base = rstto_file_get_display_name (b);
614 615 616 617 618
    guint  ac;
    guint  bc;
    const gchar *ap = a_base;
    const gchar *bp = b_base;

Stephan Arts's avatar
Stephan Arts committed
619
    gint result = 0;
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
    guint a_num = 0;
    guint b_num = 0;

    /* try simple (fast) ASCII comparison first */
    for (;; ++ap, ++bp)
    {
        /* check if the characters differ or we have a non-ASCII char
         */
        ac = *((const guchar *) ap);
        bc = *((const guchar *) bp);
        if (ac != bc || ac == 0 || ac > 127)
            break;
    }

    /* fallback to Unicode comparison */
    if (G_UNLIKELY (ac > 127 || bc > 127))
    {
        for (;; ap = g_utf8_next_char (ap), bp = g_utf8_next_char (bp))
        {
            /* check if characters differ or end of string */
            ac = g_utf8_get_char (ap);
            bc = g_utf8_get_char (bp);
            if (ac != bc || ac == 0)
                break;
        }
    }
Stephan Arts's avatar
Stephan Arts committed
646

647
    /* If both strings are equal, we're done */
Stephan Arts's avatar
Stephan Arts committed
648 649 650 651 652
    if (ac == bc)
    {
        return 0;
    }
    else
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    {
        if (G_UNLIKELY (g_ascii_isdigit (ac) || g_ascii_isdigit (bc)))
        {
            /* if both strings differ in a digit, we use a smarter comparison
             * to get sorting 'file1', 'file5', 'file10' done the right way.
             */
            if (g_ascii_isdigit (ac) && g_ascii_isdigit (bc))
            {
                a_num = strtoul (ap, NULL, 10); 
                b_num = strtoul (bp, NULL, 10); 

                if (a_num < b_num)
                    result = -1;
                if (a_num > b_num)
                    result = 1;
            }
669 670 671 672 673 674 675 676 677 678 679 680 681 682

            if (ap > a_base &&
                bp > b_base &&
                g_ascii_isdigit (*(ap -1)) &&
                g_ascii_isdigit (*(bp -1)) )
            {
                a_num = strtoul (ap-1, NULL, 10); 
                b_num = strtoul (bp-1, NULL, 10); 

                if (a_num < b_num)
                    result = -1;
                if (a_num > b_num)
                    result = 1;
            }
683 684
        }
    }
Stephan Arts's avatar
Stephan Arts committed
685

686 687 688 689
    if (result == 0)
    {
        if (ac > bc)
            result = 1;
Stephan Arts's avatar
Stephan Arts committed
690
        if (ac < bc)
691 692 693 694
            result = -1;
    }


Stephan Arts's avatar
Stephan Arts committed
695 696 697 698 699 700 701 702
    return result;
}

/**
 * cb_rstto_image_list_exif_date_compare_func:
 * @a:
 * @b:
 *
703
 * TODO: Use EXIF data if available, not the last-modification-time.
Stephan Arts's avatar
Stephan Arts committed
704 705 706 707
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
708
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
709
{
710 711 712 713 714 715 716 717
    guint64 a_t = rstto_file_get_modified_time (a);
    guint64 b_t = rstto_file_get_modified_time (b);

    if (a_t < b_t)
    {
        return -1;
    }
    return 1;
718
}