image_list.c 19.3 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 26 27 28 29
 */

#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
30
#include "file.h"
31
#include "image_list.h"
32
#include "settings.h"
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

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

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

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

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

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

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
92
rstto_image_list_get_type (void)
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 118 119
{
    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)
{
120

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

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

    image_list = g_object_new(RSTTO_TYPE_IMAGE_LIST, NULL);

    return image_list;
}

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

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

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

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

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

241
    iter = rstto_image_list_iter_new (image_list, file);
242

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

245 246 247 248 249
    return iter;
}


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

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

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

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

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

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

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



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

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

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

    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;
    }
402 403 404
}

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

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

    return iter;
}

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

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

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

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


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

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

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

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

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

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

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

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

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

        g_object_unref (settings);
504 505 506 507 508
    }

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

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

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

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

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

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

        g_object_unref (settings);
543
    }
544

545 546 547 548 549 550
    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)
{
551
    RsttoImageListIter *new_iter = rstto_image_list_iter_new (iter->priv->image_list, iter->priv->file);
Stephan Arts's avatar
Stephan Arts committed
552
    rstto_image_list_iter_set_position (new_iter, rstto_image_list_iter_get_position(iter));
553 554 555

    return new_iter;
}
556 557 558 559

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

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

570 571 572 573
    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);
    }
574 575
}

Stephan Arts's avatar
Stephan Arts committed
576 577 578 579
/***********************/
/*  Compare Functions  */
/***********************/

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

void
rstto_image_list_set_sort_by_date (RsttoImageList *image_list)
{
589
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_exif_date_compare_func);
590 591
}

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

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

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

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

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


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

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