image_list.c 22.9 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 "util.h"
Stephan Arts's avatar
Stephan Arts committed
32
#include "file.h"
33
#include "image_list.h"
34
#include "settings.h"
35 36 37 38 39 40 41 42

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

Stephan Arts's avatar
Stephan Arts committed
43 44 45 46 47 48 49 50
static void
cb_file_monitor_changed (
        GFileMonitor      *monitor,
        GFile             *file,
        GFile             *other_file,
        GFileMonitorEvent  event_type,
        gpointer           user_data );

51 52 53 54 55 56 57
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);

58 59 60 61 62 63 64 65 66 67 68 69 70 71
static gboolean
iter_next (
        RsttoImageListIter *iter,
        gboolean sticky);
static gboolean
iter_previous (
        RsttoImageListIter *iter,
        gboolean sticky);
static void 
iter_set_position (
        RsttoImageListIter *iter,
        gint pos,
        gboolean sticky);

72 73 74
static RsttoImageListIter * rstto_image_list_iter_new ();

static gint
Stephan Arts's avatar
Stephan Arts committed
75
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b);
Stephan Arts's avatar
Stephan Arts committed
76
static gint
Stephan Arts's avatar
Stephan Arts committed
77
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b);
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

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,
93
    RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE,
94 95 96 97 98 99
    RSTTO_IMAGE_LIST_ITER_SIGNAL_COUNT
};

struct _RsttoImageListIterPriv
{
    RsttoImageList *image_list;
100 101 102 103
    RsttoFile      *file;
    
    /* This is set if the iter-position is chosen by the user */
    gboolean        sticky; 
104 105 106 107
};

struct _RsttoImageListPriv
{
Stephan Arts's avatar
Stephan Arts committed
108
    GFileMonitor *monitor;
Stephan Arts's avatar
Stephan Arts committed
109

Stephan Arts's avatar
Stephan Arts committed
110 111 112 113 114
    GList        *images;
    gint          n_images;

    GSList       *iterators;
    GCompareFunc  cb_rstto_image_list_compare_func;
115 116 117 118 119 120
};

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
121
rstto_image_list_get_type (void)
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
{
    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)
{
149

150
    image_list->priv = g_new0 (RsttoImageListPriv, 1);
Stephan Arts's avatar
Stephan Arts committed
151
    image_list->priv->cb_rstto_image_list_compare_func = (GCompareFunc)cb_rstto_image_list_image_name_compare_func;
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 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
}

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
206
rstto_image_list_new (void)
207 208 209 210 211 212 213 214 215
{
    RsttoImageList *image_list;

    image_list = g_object_new(RSTTO_TYPE_IMAGE_LIST, NULL);

    return image_list;
}

gboolean
Stephan Arts's avatar
Stephan Arts committed
216
rstto_image_list_add_file (RsttoImageList *image_list, RsttoFile *file, GError **error)
217
{
Stephan Arts's avatar
Stephan Arts committed
218
    GList *image_iter = g_list_find (image_list->priv->images, file);
219
    GSList *iter = image_list->priv->iterators;
220

Stephan Arts's avatar
Stephan Arts committed
221 222
    if (!image_iter)
    {
223
        if (file)
Stephan Arts's avatar
Stephan Arts committed
224
        {
225 226
            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
227 228
            image_list->priv->n_images++;

229
            g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE], 0, file, NULL);
230 231
            /** TODO: update all iterators */
            while (iter)
Stephan Arts's avatar
Stephan Arts committed
232
            {
233
                if (FALSE == RSTTO_IMAGE_LIST_ITER(iter->data)->priv->sticky)
Stephan Arts's avatar
Stephan Arts committed
234
                {
235 236
                    g_debug("find file");
                    rstto_image_list_iter_find_file (iter->data, file);
Stephan Arts's avatar
Stephan Arts committed
237
                }
238
                iter = g_slist_next (iter);
Stephan Arts's avatar
Stephan Arts committed
239
            }
Stephan Arts's avatar
Stephan Arts committed
240
            return TRUE;
Stephan Arts's avatar
Stephan Arts committed
241
        }
Stephan Arts's avatar
Stephan Arts committed
242
        return FALSE;
243
    }
Stephan Arts's avatar
Stephan Arts committed
244 245 246
    g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE], 0, image_iter->data, NULL);

    return TRUE;
247 248 249 250 251 252 253 254
}

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
255 256 257 258 259 260 261 262
/**
 * rstto_image_list_get_iter:
 * @image_list:
 *
 * TODO: track iterators
 *
 * return iter;
 */
263 264 265
RsttoImageListIter *
rstto_image_list_get_iter (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
266
    RsttoFile *file = NULL;
Stephan Arts's avatar
Stephan Arts committed
267
    RsttoImageListIter *iter = NULL;
268
    if (image_list->priv->images)
269
        file = image_list->priv->images->data;
270

271
    iter = rstto_image_list_iter_new (image_list, file);
272

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

275 276 277 278 279
    return iter;
}


void
Stephan Arts's avatar
Stephan Arts committed
280
rstto_image_list_remove_file (RsttoImageList *image_list, RsttoFile *file)
281
{
Stephan Arts's avatar
Stephan Arts committed
282
    GSList *iter = NULL;
Stephan Arts's avatar
Stephan Arts committed
283
    RsttoFile *afile = NULL;
Stephan Arts's avatar
Stephan Arts committed
284

285
    if (g_list_find(image_list->priv->images, file))
286
    {
Stephan Arts's avatar
Stephan Arts committed
287

Stephan Arts's avatar
Stephan Arts committed
288
        iter = image_list->priv->iterators;
Stephan Arts's avatar
Stephan Arts committed
289 290
        while (iter)
        {
Stephan Arts's avatar
Stephan Arts committed
291
            if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
Stephan Arts's avatar
Stephan Arts committed
292
            {
293
                if (rstto_image_list_iter_get_position (iter->data) == rstto_image_list_get_n_images (image_list)-1)
294
                {
295
                    iter_previous (iter->data, FALSE);
296 297 298
                }
                else
                {
299
                    iter_next (iter->data, FALSE);
300
                }
301 302 303 304
                /* 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
305
                if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
306
                {
307
                    ((RsttoImageListIter *)(iter->data))->priv->file = NULL;
308
                    g_signal_emit (G_OBJECT (iter->data), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
309
                }
Stephan Arts's avatar
Stephan Arts committed
310 311 312
            }
            iter = g_slist_next (iter);
        }
313

314
        image_list->priv->images = g_list_remove (image_list->priv->images, file);
315 316 317
        iter = image_list->priv->iterators;
        while (iter)
        {
318 319
            afile = rstto_image_list_iter_get_file(iter->data);
            if (NULL != afile)
320
            {
Stephan Arts's avatar
Stephan Arts committed
321
                if (rstto_file_equal(afile, file))
322
                {
323
                    iter_next (iter->data, FALSE);
324
                }
325 326 327 328
            }
            iter = g_slist_next (iter);
        }

329 330
        g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_IMAGE], 0, file, NULL);
        g_object_unref(file);
331 332 333 334 335 336
    }
}

void
rstto_image_list_remove_all (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
337
    GSList *iter = NULL;
338 339 340
    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
341

Stephan Arts's avatar
Stephan Arts committed
342
    iter = image_list->priv->iterators;
Stephan Arts's avatar
Stephan Arts committed
343 344
    while (iter)
    {
345
        iter_set_position (iter->data, -1, FALSE);
Stephan Arts's avatar
Stephan Arts committed
346 347 348
        iter = g_slist_next (iter);
    }
    g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_ALL], 0, NULL);
349 350
}

Stephan Arts's avatar
Stephan Arts committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
void
rstto_image_list_monitor_dir (
        RsttoImageList *image_list,
        GFile *dir )
{
    GFileMonitor *monitor = NULL;

    if ( NULL != image_list->priv->monitor )
    {
        g_object_unref (image_list->priv->monitor);
        image_list->priv->monitor = NULL;
    }

    monitor = g_file_monitor_directory (
            dir,
            G_FILE_MONITOR_NONE,
            NULL,
            NULL);

    g_signal_connect (
            G_OBJECT(monitor),
            "changed",
            G_CALLBACK (cb_file_monitor_changed),
            image_list);

    image_list->priv->monitor = monitor;
}

static void
cb_file_monitor_changed (
        GFileMonitor      *monitor,
        GFile             *file,
        GFile             *other_file,
        GFileMonitorEvent  event_type,
        gpointer           user_data )
{
    RsttoImageList *image_list = RSTTO_IMAGE_LIST (user_data);
    RsttoFile *r_file = rstto_file_new (file);

    g_return_if_fail ( monitor == image_list->priv->monitor);

    switch ( event_type )
    {
        case G_FILE_MONITOR_EVENT_DELETED:
            g_debug("file deleted");
            rstto_image_list_remove_file ( image_list, r_file );
            r_file = NULL;
            break;
        case G_FILE_MONITOR_EVENT_CREATED:
            g_debug("file created");
            rstto_image_list_add_file (image_list, r_file, NULL);
            r_file = NULL;
            break;
        case G_FILE_MONITOR_EVENT_MOVED:
            g_debug("file moved");
            rstto_image_list_remove_file ( image_list, r_file );
            r_file = rstto_file_new (other_file);
            rstto_image_list_add_file (image_list, r_file, NULL);
            r_file = NULL;
            break;
        default:
            break;
    }

    if ( NULL != r_file )
    {
        g_object_unref (r_file);
    }
}

421 422 423


GType
Stephan Arts's avatar
Stephan Arts committed
424
rstto_image_list_iter_get_type (void)
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
{
    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;

464 465 466 467 468 469 470 471 472 473 474
    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);

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
    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);
492
    if (iter->priv->file)
493
    {
494
        iter->priv->file = NULL;
495
    }
Stephan Arts's avatar
Stephan Arts committed
496 497 498 499 500 501

    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;
    }
502 503 504
}

static RsttoImageListIter *
Stephan Arts's avatar
Stephan Arts committed
505
rstto_image_list_iter_new (RsttoImageList *nav, RsttoFile *file)
506 507 508 509
{
    RsttoImageListIter *iter;

    iter = g_object_new(RSTTO_TYPE_IMAGE_LIST_ITER, NULL);
510
    iter->priv->file = file;
511 512 513 514 515 516
    iter->priv->image_list = nav;

    return iter;
}

gboolean
Stephan Arts's avatar
Stephan Arts committed
517
rstto_image_list_iter_find_file (RsttoImageListIter *iter, RsttoFile *file)
518
{
519
    gint pos = g_list_index (iter->priv->image_list->priv->images, file);
520 521
    if (pos > -1)
    {
522 523
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

524
        if (iter->priv->file)
525
        {
526
            iter->priv->file = NULL;
527
        }
528
        iter->priv->file = file;
529
        iter->priv->sticky = TRUE;
530 531 532 533 534 535 536 537 538 539 540

        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)
{
541
    if ( NULL == iter->priv->file )
542
    {
543
        return -1;
544
    }
545
    return g_list_index (iter->priv->image_list->priv->images, iter->priv->file);
546 547
}

Stephan Arts's avatar
Stephan Arts committed
548
RsttoFile *
549
rstto_image_list_iter_get_file (RsttoImageListIter *iter)
550
{
551
    return iter->priv->file;
552 553
}

554 555 556 557 558
static void
iter_set_position (
        RsttoImageListIter *iter,
        gint pos,
        gboolean sticky )
559
{
560 561
    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

562
    if (iter->priv->file)
563
    {
564
        iter->priv->file = NULL;
565 566
    }

Stephan Arts's avatar
Stephan Arts committed
567
    if (pos >= 0)
Stephan Arts's avatar
Stephan Arts committed
568
    {
569
        iter->priv->file = g_list_nth_data (iter->priv->image_list->priv->images, pos); 
Stephan Arts's avatar
Stephan Arts committed
570
    }
571

572 573 574
    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
}

575 576 577 578 579 580 581 582 583 584
void
rstto_image_list_iter_set_position (RsttoImageListIter *iter, gint pos)
{
    iter_set_position ( iter, pos, TRUE );
}

static gboolean
iter_next (
        RsttoImageListIter *iter,
        gboolean sticky)
585
{
586
    GList *position = NULL;
Stephan Arts's avatar
Stephan Arts committed
587
    RsttoSettings *settings = NULL;
588
    RsttoFile *file = iter->priv->file;
589
    gboolean ret_val = FALSE;
590 591 592

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

593
    if (iter->priv->file)
594
    {
595 596
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
597 598
    }

599 600
    iter->priv->sticky = sticky;

601 602
    position = g_list_next (position);
    if (position)
603
    {
604
        iter->priv->file = position->data; 
605 606 607 608

        /* We could move forward, set ret_val to TRUE */
        ret_val = TRUE;
    }
609 610
    else
    {
Stephan Arts's avatar
Stephan Arts committed
611 612 613
        settings = rstto_settings_new();

        if (rstto_settings_get_boolean_property (settings, "wrap-images"))
614
        {
615
            position = g_list_first (iter->priv->image_list->priv->images);
616 617 618 619 620 621

            /* We could move forward, wrapped back to the start of the
             * list, set ret_val to TRUE
             */
            ret_val = TRUE;
        }
622 623 624
        else
            position = g_list_last (iter->priv->image_list->priv->images);

625
        if (position)
626
            iter->priv->file = position->data; 
627
        else
628
            iter->priv->file = NULL;
Stephan Arts's avatar
Stephan Arts committed
629 630

        g_object_unref (settings);
631 632
    }

633 634 635 636
    if (file != iter->priv->file)
    {
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
    }
637 638

    return ret_val;
639 640
}

641
gboolean
642 643 644 645 646 647 648 649 650
rstto_image_list_iter_next (RsttoImageListIter *iter)
{
    return iter_next (iter, TRUE);
}

static gboolean
iter_previous (
        RsttoImageListIter *iter,
        gboolean sticky)
651
{
652
    GList *position = NULL;
Stephan Arts's avatar
Stephan Arts committed
653
    RsttoSettings *settings = NULL;
654
    RsttoFile *file = iter->priv->file;
655
    gboolean ret_val = FALSE;
656 657 658

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

659
    if (iter->priv->file)
660
    {
661 662
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
663 664
    }

665 666
    iter->priv->sticky = sticky;

667 668
    position = g_list_previous (position);
    if (position)
669
    {
670
        iter->priv->file = position->data; 
671
    }
672 673
    else
    {
Stephan Arts's avatar
Stephan Arts committed
674 675 676
        settings = rstto_settings_new();

        if (rstto_settings_get_boolean_property (settings, "wrap-images"))
677 678 679 680
            position = g_list_last (iter->priv->image_list->priv->images);
        else
            position = g_list_first (iter->priv->image_list->priv->images);

681
        if (position)
682
            iter->priv->file = position->data; 
683
        else
684
            iter->priv->file = NULL;
Stephan Arts's avatar
Stephan Arts committed
685 686

        g_object_unref (settings);
687
    }
688

689 690 691 692
    if (file != iter->priv->file)
    {
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
    }
693 694

    return ret_val;
695 696 697 698 699 700 701

}

gboolean
rstto_image_list_iter_previous (RsttoImageListIter *iter)
{
    return iter_previous (iter, TRUE);
702 703 704 705 706
}

RsttoImageListIter *
rstto_image_list_iter_clone (RsttoImageListIter *iter)
{
707
    RsttoImageListIter *new_iter = rstto_image_list_iter_new (iter->priv->image_list, iter->priv->file);
Stephan Arts's avatar
Stephan Arts committed
708
    rstto_image_list_iter_set_position (new_iter, rstto_image_list_iter_get_position(iter));
709 710 711

    return new_iter;
}
712 713 714 715

GCompareFunc
rstto_image_list_get_compare_func (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
716 717 718
    return (GCompareFunc)image_list->priv->cb_rstto_image_list_compare_func;
}

719 720 721
void
rstto_image_list_set_compare_func (RsttoImageList *image_list, GCompareFunc func)
{
722
    GSList *iter = NULL;
723
    image_list->priv->cb_rstto_image_list_compare_func = func;
724
    image_list->priv->images = g_list_sort (image_list->priv->images,  func);
725

726 727 728 729
    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);
    }
730 731
}

Stephan Arts's avatar
Stephan Arts committed
732 733 734 735
/***********************/
/*  Compare Functions  */
/***********************/

736 737 738
void
rstto_image_list_set_sort_by_name (RsttoImageList *image_list)
{
739
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_image_name_compare_func);
740 741 742 743 744
}

void
rstto_image_list_set_sort_by_date (RsttoImageList *image_list)
{
745
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_exif_date_compare_func);
746 747
}

Stephan Arts's avatar
Stephan Arts committed
748 749 750 751 752 753 754 755 756
/**
 * cb_rstto_image_list_image_name_compare_func:
 * @a:
 * @b:
 *
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
757
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
758
{
Stephan Arts's avatar
Stephan Arts committed
759 760
    const gchar *a_base = rstto_file_get_display_name (a);
    const gchar *b_base = rstto_file_get_display_name (b);
761 762 763 764 765
    guint  ac;
    guint  bc;
    const gchar *ap = a_base;
    const gchar *bp = b_base;

Stephan Arts's avatar
Stephan Arts committed
766
    gint result = 0;
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
    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
793

794
    /* If both strings are equal, we're done */
Stephan Arts's avatar
Stephan Arts committed
795 796 797 798 799
    if (ac == bc)
    {
        return 0;
    }
    else
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
    {
        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;
            }
816 817 818 819 820 821 822 823 824 825 826 827 828 829

            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;
            }
830 831
        }
    }
Stephan Arts's avatar
Stephan Arts committed
832

833 834 835 836
    if (result == 0)
    {
        if (ac > bc)
            result = 1;
Stephan Arts's avatar
Stephan Arts committed
837
        if (ac < bc)
838 839 840 841
            result = -1;
    }


Stephan Arts's avatar
Stephan Arts committed
842 843 844 845 846 847 848 849
    return result;
}

/**
 * cb_rstto_image_list_exif_date_compare_func:
 * @a:
 * @b:
 *
850
 * TODO: Use EXIF data if available, not the last-modification-time.
Stephan Arts's avatar
Stephan Arts committed
851 852 853 854
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
855
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
856
{
857 858 859 860 861 862 863 864
    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;
865
}
866 867 868 869 870 871 872

gboolean
rstto_image_list_iter_get_sticky (
        RsttoImageListIter *iter)
{
    return iter->priv->sticky;
}