image_list.c 19.2 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 17 18 19 20 21 22 23 24 25 26 27
 *  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.
 */

#include <config.h>

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

#include <string.h>

#include <libexif/exif-data.h>

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

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
49
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b);
Stephan Arts's avatar
Stephan Arts committed
50
static gint
Stephan Arts's avatar
Stephan Arts committed
51
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b);
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

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,
67
    RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE,
68 69 70 71 72 73
    RSTTO_IMAGE_LIST_ITER_SIGNAL_COUNT
};

struct _RsttoImageListIterPriv
{
    RsttoImageList *image_list;
Stephan Arts's avatar
Stephan Arts committed
74
    RsttoFile *file;
75 76 77 78 79 80
};

struct _RsttoImageListPriv
{
    GList *images;
    gint n_images;
Stephan Arts's avatar
Stephan Arts committed
81 82

    GSList *iterators;
Stephan Arts's avatar
Stephan Arts committed
83
    GCompareFunc cb_rstto_image_list_compare_func;
84 85 86 87 88 89
};

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
90
rstto_image_list_get_type (void)
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
{
    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)
{
118

119
    image_list->priv = g_new0 (RsttoImageListPriv, 1);
Stephan Arts's avatar
Stephan Arts committed
120
    image_list->priv->cb_rstto_image_list_compare_func = (GCompareFunc)cb_rstto_image_list_image_name_compare_func;
121 122 123 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
}

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
175
rstto_image_list_new (void)
176 177 178 179 180 181 182 183 184
{
    RsttoImageList *image_list;

    image_list = g_object_new(RSTTO_TYPE_IMAGE_LIST, NULL);

    return image_list;
}

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

Stephan Arts's avatar
Stephan Arts committed
189 190
    if (!image_iter)
    {
191
        if (file)
Stephan Arts's avatar
Stephan Arts committed
192
        {
193 194
            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
195 196
            image_list->priv->n_images++;

197
            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
198
            if (image_list->priv->n_images == 1)
Stephan Arts's avatar
Stephan Arts committed
199
            {
Stephan Arts's avatar
Stephan Arts committed
200 201 202 203 204 205 206
                /** 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
207
            }
Stephan Arts's avatar
Stephan Arts committed
208
            return TRUE;
Stephan Arts's avatar
Stephan Arts committed
209
        }
Stephan Arts's avatar
Stephan Arts committed
210
        return FALSE;
211
    }
Stephan Arts's avatar
Stephan Arts committed
212 213 214
    g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE], 0, image_iter->data, NULL);

    return TRUE;
215 216 217 218 219 220 221 222
}

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
223 224 225 226 227 228 229 230
/**
 * rstto_image_list_get_iter:
 * @image_list:
 *
 * TODO: track iterators
 *
 * return iter;
 */
231 232 233
RsttoImageListIter *
rstto_image_list_get_iter (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
234
    RsttoFile *file = NULL;
Stephan Arts's avatar
Stephan Arts committed
235
    RsttoImageListIter *iter = NULL;
236
    if (image_list->priv->images)
237
        file = image_list->priv->images->data;
238

239
    iter = rstto_image_list_iter_new (image_list, file);
240

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

243 244 245 246 247
    return iter;
}


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

253
    if (g_list_find(image_list->priv->images, file))
254
    {
Stephan Arts's avatar
Stephan Arts committed
255

Stephan Arts's avatar
Stephan Arts committed
256
        iter = image_list->priv->iterators;
Stephan Arts's avatar
Stephan Arts committed
257 258
        while (iter)
        {
Stephan Arts's avatar
Stephan Arts committed
259
            if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
Stephan Arts's avatar
Stephan Arts committed
260
            {
261
                if (rstto_image_list_iter_get_position (iter->data) == rstto_image_list_get_n_images (image_list)-1)
262
                {
263
                    rstto_image_list_iter_previous (iter->data);
264 265 266
                }
                else
                {
267
                    rstto_image_list_iter_next (iter->data);
268
                }
269 270 271 272
                /* 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
273
                if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
274
                {
275
                    ((RsttoImageListIter *)(iter->data))->priv->file = NULL;
276
                    g_signal_emit (G_OBJECT (iter->data), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
277
                }
Stephan Arts's avatar
Stephan Arts committed
278 279 280
            }
            iter = g_slist_next (iter);
        }
281

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

297 298
        g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_IMAGE], 0, file, NULL);
        g_object_unref(file);
299 300 301 302 303 304
    }
}

void
rstto_image_list_remove_all (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
305
    GSList *iter = NULL;
306 307 308
    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
309

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



GType
Stephan Arts's avatar
Stephan Arts committed
322
rstto_image_list_iter_get_type (void)
323 324 325 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
{
    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;

362 363 364 365 366 367 368 369 370 371 372
    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);

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
    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);
390
    if (iter->priv->file)
391
    {
392
        iter->priv->file = NULL;
393
    }
Stephan Arts's avatar
Stephan Arts committed
394 395 396 397 398 399

    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;
    }
400 401 402
}

static RsttoImageListIter *
Stephan Arts's avatar
Stephan Arts committed
403
rstto_image_list_iter_new (RsttoImageList *nav, RsttoFile *file)
404 405 406 407
{
    RsttoImageListIter *iter;

    iter = g_object_new(RSTTO_TYPE_IMAGE_LIST_ITER, NULL);
408
    iter->priv->file = file;
409 410 411 412 413 414
    iter->priv->image_list = nav;

    return iter;
}

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

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

        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)
{
438
    if ( NULL == iter->priv->file )
439
    {
440
        return -1;
441
    }
442
    return g_list_index (iter->priv->image_list->priv->images, iter->priv->file);
443 444
}

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


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

457
    if (iter->priv->file)
458
    {
459
        iter->priv->file = NULL;
460 461
    }

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

467 468 469
    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
470
void
471 472
rstto_image_list_iter_next (RsttoImageListIter *iter)
{
473
    GList *position = NULL;
Stephan Arts's avatar
Stephan Arts committed
474
    RsttoSettings *settings = NULL;
475 476 477

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

478
    if (iter->priv->file)
479
    {
480 481
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
482 483
    }

484 485
    position = g_list_next (position);
    if (position)
486
        iter->priv->file = position->data; 
487 488
    else
    {
Stephan Arts's avatar
Stephan Arts committed
489 490 491
        settings = rstto_settings_new();

        if (rstto_settings_get_boolean_property (settings, "wrap-images"))
492 493 494 495
            position = g_list_first (iter->priv->image_list->priv->images);
        else
            position = g_list_last (iter->priv->image_list->priv->images);

496
        if (position)
497
            iter->priv->file = position->data; 
498
        else
499
            iter->priv->file = NULL;
Stephan Arts's avatar
Stephan Arts committed
500 501

        g_object_unref (settings);
502 503 504 505 506
    }

    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
507
void
508 509
rstto_image_list_iter_previous (RsttoImageListIter *iter)
{
510
    GList *position = NULL;
Stephan Arts's avatar
Stephan Arts committed
511
    RsttoSettings *settings = NULL;
512 513 514

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

515
    if (iter->priv->file)
516
    {
517 518
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
519 520
    }

521 522
    position = g_list_previous (position);
    if (position)
523
    {
524
        iter->priv->file = position->data; 
525
    }
526 527
    else
    {
Stephan Arts's avatar
Stephan Arts committed
528 529 530
        settings = rstto_settings_new();

        if (rstto_settings_get_boolean_property (settings, "wrap-images"))
531 532 533 534
            position = g_list_last (iter->priv->image_list->priv->images);
        else
            position = g_list_first (iter->priv->image_list->priv->images);

535
        if (position)
536
            iter->priv->file = position->data; 
537
        else
538
            iter->priv->file = NULL;
Stephan Arts's avatar
Stephan Arts committed
539 540

        g_object_unref (settings);
541
    }
542

543 544 545 546 547 548
    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
}

RsttoImageListIter *
rstto_image_list_iter_clone (RsttoImageListIter *iter)
{
549
    RsttoImageListIter *new_iter = rstto_image_list_iter_new (iter->priv->image_list, iter->priv->file);
Stephan Arts's avatar
Stephan Arts committed
550
    rstto_image_list_iter_set_position (new_iter, rstto_image_list_iter_get_position(iter));
551 552 553

    return new_iter;
}
554 555 556 557

GCompareFunc
rstto_image_list_get_compare_func (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
558 559 560
    return (GCompareFunc)image_list->priv->cb_rstto_image_list_compare_func;
}

561 562 563
void
rstto_image_list_set_compare_func (RsttoImageList *image_list, GCompareFunc func)
{
564
    GSList *iter = NULL;
565
    image_list->priv->cb_rstto_image_list_compare_func = func;
566
    image_list->priv->images = g_list_sort (image_list->priv->images,  func);
567

568 569 570 571
    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);
    }
572 573
}

Stephan Arts's avatar
Stephan Arts committed
574 575 576 577
/***********************/
/*  Compare Functions  */
/***********************/

578 579 580
void
rstto_image_list_set_sort_by_name (RsttoImageList *image_list)
{
581
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_image_name_compare_func);
582 583 584 585 586
}

void
rstto_image_list_set_sort_by_date (RsttoImageList *image_list)
{
587
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_exif_date_compare_func);
588 589
}

Stephan Arts's avatar
Stephan Arts committed
590 591 592 593 594 595 596 597 598
/**
 * cb_rstto_image_list_image_name_compare_func:
 * @a:
 * @b:
 *
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
599
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
600
{
Stephan Arts's avatar
Stephan Arts committed
601 602
    const gchar *a_base = rstto_file_get_display_name (a);
    const gchar *b_base = rstto_file_get_display_name (b);
603 604 605 606 607
    guint  ac;
    guint  bc;
    const gchar *ap = a_base;
    const gchar *bp = b_base;

Stephan Arts's avatar
Stephan Arts committed
608
    gint result = 0;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
    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
635

636
    /* If both strings are equal, we're done */
Stephan Arts's avatar
Stephan Arts committed
637 638 639 640 641
    if (ac == bc)
    {
        return 0;
    }
    else
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
    {
        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;
            }
658 659 660 661 662 663 664 665 666 667 668 669 670 671

            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;
            }
672 673
        }
    }
Stephan Arts's avatar
Stephan Arts committed
674

675 676 677 678
    if (result == 0)
    {
        if (ac > bc)
            result = 1;
Stephan Arts's avatar
Stephan Arts committed
679
        if (ac < bc)
680 681 682 683
            result = -1;
    }


Stephan Arts's avatar
Stephan Arts committed
684 685 686 687 688 689 690 691
    return result;
}

/**
 * cb_rstto_image_list_exif_date_compare_func:
 * @a:
 * @b:
 *
692
 * TODO: Use EXIF data if available, not the last-modification-time.
Stephan Arts's avatar
Stephan Arts committed
693 694 695 696
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
697
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
698
{
699 700 701 702 703 704 705 706
    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;
707
}