image_list.c 26.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 "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
static void
cb_rstto_wrap_images_changed (
        GObject *settings,
        GParamSpec *pspec,
        gpointer user_data);

64 65 66 67 68 69 70 71 72
static void
rstto_image_list_monitor_dir (
        RsttoImageList *image_list,
        GFile *dir );

static void
rstto_image_list_remove_all (
        RsttoImageList *image_list);

73 74 75 76
static gboolean
iter_next (
        RsttoImageListIter *iter,
        gboolean sticky);
77

78 79 80 81
static gboolean
iter_previous (
        RsttoImageListIter *iter,
        gboolean sticky);
82

83 84 85 86 87 88
static void 
iter_set_position (
        RsttoImageListIter *iter,
        gint pos,
        gboolean sticky);

89 90 91
static RsttoImageListIter * rstto_image_list_iter_new ();

static gint
Stephan Arts's avatar
Stephan Arts committed
92
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b);
Stephan Arts's avatar
Stephan Arts committed
93
static gint
Stephan Arts's avatar
Stephan Arts committed
94
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109

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,
110
    RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE,
111 112 113 114 115 116
    RSTTO_IMAGE_LIST_ITER_SIGNAL_COUNT
};

struct _RsttoImageListIterPriv
{
    RsttoImageList *image_list;
117 118 119 120
    RsttoFile      *file;
    
    /* This is set if the iter-position is chosen by the user */
    gboolean        sticky; 
121 122 123 124
};

struct _RsttoImageListPriv
{
Stephan Arts's avatar
Stephan Arts committed
125
    GFileMonitor *monitor;
126
    RsttoSettings *settings;
Stephan Arts's avatar
Stephan Arts committed
127

Stephan Arts's avatar
Stephan Arts committed
128 129 130 131 132
    GList        *images;
    gint          n_images;

    GSList       *iterators;
    GCompareFunc  cb_rstto_image_list_compare_func;
133 134

    gboolean      wrap_images;
135 136 137 138 139 140
};

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
141
rstto_image_list_get_type (void)
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
{
    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)
{
169

170
    image_list->priv = g_new0 (RsttoImageListPriv, 1);
171 172
    image_list->priv->settings = rstto_settings_new ();

Stephan Arts's avatar
Stephan Arts committed
173
    image_list->priv->cb_rstto_image_list_compare_func = (GCompareFunc)cb_rstto_image_list_image_name_compare_func;
174 175 176 177 178 179 180 181 182 183 184

    image_list->priv->wrap_images = rstto_settings_get_boolean_property (
            image_list->priv->settings,
            "wrap-images");

    g_signal_connect (
            G_OBJECT(image_list->priv->settings),
            "notify::wrap-images",
            G_CALLBACK (cb_rstto_wrap_images_changed),
            image_list);

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
}

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)
{
235 236 237 238 239 240 241 242 243 244 245 246
    RsttoImageList *image_list = RSTTO_IMAGE_LIST(object);
    if (NULL != image_list->priv)
    {
        if (image_list->priv->settings)
        {
            g_object_unref (image_list->priv->settings);
            image_list->priv->settings = NULL;
        }
        
        g_free (image_list->priv);
        image_list->priv = NULL;
    }
247 248 249
}

RsttoImageList *
Stephan Arts's avatar
Stephan Arts committed
250
rstto_image_list_new (void)
251 252 253 254 255 256 257 258 259
{
    RsttoImageList *image_list;

    image_list = g_object_new(RSTTO_TYPE_IMAGE_LIST, NULL);

    return image_list;
}

gboolean
Stephan Arts's avatar
Stephan Arts committed
260
rstto_image_list_add_file (RsttoImageList *image_list, RsttoFile *file, GError **error)
261
{
Stephan Arts's avatar
Stephan Arts committed
262
    GList *image_iter = g_list_find (image_list->priv->images, file);
263
    GSList *iter = image_list->priv->iterators;
264

Stephan Arts's avatar
Stephan Arts committed
265 266
    if (!image_iter)
    {
267
        if (file)
Stephan Arts's avatar
Stephan Arts committed
268
        {
269 270
            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
271 272
            image_list->priv->n_images++;

273
            g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE], 0, file, NULL);
274 275
            /** TODO: update all iterators */
            while (iter)
Stephan Arts's avatar
Stephan Arts committed
276
            {
277
                if (FALSE == RSTTO_IMAGE_LIST_ITER(iter->data)->priv->sticky)
Stephan Arts's avatar
Stephan Arts committed
278
                {
279
                    rstto_image_list_iter_find_file (iter->data, file);
Stephan Arts's avatar
Stephan Arts committed
280
                }
281
                iter = g_slist_next (iter);
Stephan Arts's avatar
Stephan Arts committed
282
            }
Stephan Arts's avatar
Stephan Arts committed
283
            return TRUE;
Stephan Arts's avatar
Stephan Arts committed
284
        }
Stephan Arts's avatar
Stephan Arts committed
285
        return FALSE;
286
    }
Stephan Arts's avatar
Stephan Arts committed
287 288 289
    g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_NEW_IMAGE], 0, image_iter->data, NULL);

    return TRUE;
290 291 292 293 294 295 296 297
}

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
298 299 300 301 302 303 304 305
/**
 * rstto_image_list_get_iter:
 * @image_list:
 *
 * TODO: track iterators
 *
 * return iter;
 */
306 307 308
RsttoImageListIter *
rstto_image_list_get_iter (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
309
    RsttoFile *file = NULL;
Stephan Arts's avatar
Stephan Arts committed
310
    RsttoImageListIter *iter = NULL;
311
    if (image_list->priv->images)
312
        file = image_list->priv->images->data;
313

314
    iter = rstto_image_list_iter_new (image_list, file);
315

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

318 319 320 321 322
    return iter;
}


void
Stephan Arts's avatar
Stephan Arts committed
323
rstto_image_list_remove_file (RsttoImageList *image_list, RsttoFile *file)
324
{
Stephan Arts's avatar
Stephan Arts committed
325
    GSList *iter = NULL;
Stephan Arts's avatar
Stephan Arts committed
326
    RsttoFile *afile = NULL;
Stephan Arts's avatar
Stephan Arts committed
327

328
    if (g_list_find(image_list->priv->images, file))
329
    {
Stephan Arts's avatar
Stephan Arts committed
330

Stephan Arts's avatar
Stephan Arts committed
331
        iter = image_list->priv->iterators;
Stephan Arts's avatar
Stephan Arts committed
332 333
        while (iter)
        {
Stephan Arts's avatar
Stephan Arts committed
334
            if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
Stephan Arts's avatar
Stephan Arts committed
335
            {
336
                if (rstto_image_list_iter_get_position (iter->data) == rstto_image_list_get_n_images (image_list)-1)
337
                {
338
                    iter_previous (iter->data, FALSE);
339 340 341
                }
                else
                {
342
                    iter_next (iter->data, FALSE);
343
                }
344 345 346 347
                /* 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
348
                if (rstto_file_equal(rstto_image_list_iter_get_file (iter->data), file))
349
                {
350
                    ((RsttoImageListIter *)(iter->data))->priv->file = NULL;
351
                    g_signal_emit (G_OBJECT (iter->data), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
352
                }
Stephan Arts's avatar
Stephan Arts committed
353 354 355
            }
            iter = g_slist_next (iter);
        }
356

357
        image_list->priv->images = g_list_remove (image_list->priv->images, file);
358 359 360
        iter = image_list->priv->iterators;
        while (iter)
        {
361 362
            afile = rstto_image_list_iter_get_file(iter->data);
            if (NULL != afile)
363
            {
Stephan Arts's avatar
Stephan Arts committed
364
                if (rstto_file_equal(afile, file))
365
                {
366
                    iter_next (iter->data, FALSE);
367
                }
368 369 370 371
            }
            iter = g_slist_next (iter);
        }

372 373
        g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_IMAGE], 0, file, NULL);
        g_object_unref(file);
374 375 376
    }
}

377
static void
378 379
rstto_image_list_remove_all (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
380
    GSList *iter = NULL;
381 382 383
    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
384

Stephan Arts's avatar
Stephan Arts committed
385
    iter = image_list->priv->iterators;
Stephan Arts's avatar
Stephan Arts committed
386 387
    while (iter)
    {
388
        iter_set_position (iter->data, -1, FALSE);
Stephan Arts's avatar
Stephan Arts committed
389 390 391
        iter = g_slist_next (iter);
    }
    g_signal_emit (G_OBJECT (image_list), rstto_image_list_signals[RSTTO_IMAGE_LIST_SIGNAL_REMOVE_ALL], 0, NULL);
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 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
gboolean
rstto_image_list_set_directory (
        RsttoImageList *image_list,
        GFile *dir,
        GError **error )
{
    /* Declare variables */
    GFileEnumerator *file_enumerator = NULL;
    GFileInfo *file_info;
    const gchar *filename;
    const gchar *content_type;
    GFile *child_file;

    /* Source code block */
    rstto_image_list_remove_all (image_list);

    /* Allow all images to be removed by providing NULL to dir */
    if ( NULL != dir )
    {
        file_enumerator = g_file_enumerate_children (dir, "standard::*", 0, NULL, NULL);

        if (NULL != file_enumerator)
        {
            for(file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL);
                NULL != file_info;
                file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL))
            {
                filename = g_file_info_get_name (file_info);
                content_type  = g_file_info_get_content_type (file_info);
                child_file = g_file_get_child (dir, filename);
                if (strncmp (content_type, "image/", 6) == 0)
                {
                    rstto_image_list_add_file (image_list, rstto_file_new (child_file), NULL);
                }
            }
            g_object_unref (file_enumerator);
            file_enumerator = NULL;
        }
    }

    rstto_image_list_monitor_dir ( image_list, dir );

    return TRUE;
}

static void
Stephan Arts's avatar
Stephan Arts committed
440 441 442 443 444 445 446 447 448 449 450 451
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;
    }

452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    /* Allow a monitor to be removed by providing NULL to dir */
    if ( NULL != dir )
    {
        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);
    }
Stephan Arts's avatar
Stephan Arts committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509

    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:
            rstto_image_list_remove_file ( image_list, r_file );
            r_file = NULL;
            break;
        case G_FILE_MONITOR_EVENT_CREATED:
            rstto_image_list_add_file (image_list, r_file, NULL);
            r_file = NULL;
            break;
        case G_FILE_MONITOR_EVENT_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);
    }
}

510 511 512


GType
Stephan Arts's avatar
Stephan Arts committed
513
rstto_image_list_iter_get_type (void)
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
{
    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;

553 554 555 556 557 558 559 560 561 562 563
    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);

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
    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);
581
    if (iter->priv->file)
582
    {
583
        iter->priv->file = NULL;
584
    }
Stephan Arts's avatar
Stephan Arts committed
585 586 587 588 589 590

    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;
    }
591 592 593
}

static RsttoImageListIter *
Stephan Arts's avatar
Stephan Arts committed
594
rstto_image_list_iter_new (RsttoImageList *nav, RsttoFile *file)
595 596 597 598
{
    RsttoImageListIter *iter;

    iter = g_object_new(RSTTO_TYPE_IMAGE_LIST_ITER, NULL);
599
    iter->priv->file = file;
600 601 602 603 604 605
    iter->priv->image_list = nav;

    return iter;
}

gboolean
Stephan Arts's avatar
Stephan Arts committed
606
rstto_image_list_iter_find_file (RsttoImageListIter *iter, RsttoFile *file)
607
{
608
    gint pos = g_list_index (iter->priv->image_list->priv->images, file);
609 610
    if (pos > -1)
    {
611 612
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

613
        if (iter->priv->file)
614
        {
615
            iter->priv->file = NULL;
616
        }
617
        iter->priv->file = file;
618
        iter->priv->sticky = TRUE;
619 620 621 622 623 624 625 626 627 628 629

        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)
{
630
    if ( NULL == iter->priv->file )
631
    {
632
        return -1;
633
    }
634
    return g_list_index (iter->priv->image_list->priv->images, iter->priv->file);
635 636
}

Stephan Arts's avatar
Stephan Arts committed
637
RsttoFile *
638
rstto_image_list_iter_get_file (RsttoImageListIter *iter)
639
{
640
    return iter->priv->file;
641 642
}

643 644 645 646 647
static void
iter_set_position (
        RsttoImageListIter *iter,
        gint pos,
        gboolean sticky )
648
{
649 650
    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_PREPARE_CHANGE], 0, NULL);

651
    if (iter->priv->file)
652
    {
653
        iter->priv->file = NULL;
654 655
    }

Stephan Arts's avatar
Stephan Arts committed
656
    if (pos >= 0)
Stephan Arts's avatar
Stephan Arts committed
657
    {
658
        iter->priv->file = g_list_nth_data (iter->priv->image_list->priv->images, pos); 
Stephan Arts's avatar
Stephan Arts committed
659
    }
660

661 662 663
    g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
}

664 665 666 667 668 669 670 671 672 673
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)
674
{
675
    GList *position = NULL;
676
    RsttoImageList *image_list = iter->priv->image_list;
677
    RsttoFile *file = iter->priv->file;
678
    gboolean ret_val = FALSE;
679 680 681

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

682
    if (iter->priv->file)
683
    {
684 685
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
686 687
    }

688 689
    iter->priv->sticky = sticky;

690 691
    position = g_list_next (position);
    if (position)
692
    {
693
        iter->priv->file = position->data; 
694 695 696 697

        /* We could move forward, set ret_val to TRUE */
        ret_val = TRUE;
    }
698 699
    else
    {
Stephan Arts's avatar
Stephan Arts committed
700

701
        if (TRUE == image_list->priv->wrap_images)
702
        {
703
            position = g_list_first (iter->priv->image_list->priv->images);
704 705 706 707 708 709

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

713
        if (position)
714
            iter->priv->file = position->data; 
715
        else
716
            iter->priv->file = NULL;
717 718
    }

719 720 721 722
    if (file != iter->priv->file)
    {
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
    }
723 724

    return ret_val;
725 726
}

727
gboolean
728 729 730 731 732
rstto_image_list_iter_next (RsttoImageListIter *iter)
{
    return iter_next (iter, TRUE);
}

733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
gboolean
rstto_image_list_iter_has_next (RsttoImageListIter *iter)
{
    RsttoImageList *image_list = iter->priv->image_list;

    if (image_list->priv->wrap_images)
    {
        return TRUE;
    }
    else
    {
        if (rstto_image_list_iter_get_position (iter) ==
            (rstto_image_list_get_n_images (image_list) -1))
        {
            return FALSE;
        }
        else
        {
            return TRUE;
        }
    }
}

756 757 758 759
static gboolean
iter_previous (
        RsttoImageListIter *iter,
        gboolean sticky)
760
{
761
    GList *position = NULL;
762
    RsttoImageList *image_list = iter->priv->image_list;
763
    RsttoFile *file = iter->priv->file;
764
    gboolean ret_val = FALSE;
765 766 767

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

768
    if (iter->priv->file)
769
    {
770 771
        position = g_list_find (iter->priv->image_list->priv->images, iter->priv->file);
        iter->priv->file = NULL;
772 773
    }

774 775
    iter->priv->sticky = sticky;

776 777
    position = g_list_previous (position);
    if (position)
778
    {
779
        iter->priv->file = position->data; 
780
    }
781 782
    else
    {
783 784
        if (TRUE == image_list->priv->wrap_images)
        {
785
            position = g_list_last (iter->priv->image_list->priv->images);
786
        }
787
        else
788
        {
789
            position = g_list_first (iter->priv->image_list->priv->images);
790
        }
791

792
        if (position)
793
        {
794
            iter->priv->file = position->data; 
795
        }
796
        else
797
        {
798
            iter->priv->file = NULL;
799
        }
800
    }
801

802 803 804 805
    if (file != iter->priv->file)
    {
        g_signal_emit (G_OBJECT (iter), rstto_image_list_iter_signals[RSTTO_IMAGE_LIST_ITER_SIGNAL_CHANGED], 0, NULL);
    }
806 807

    return ret_val;
808 809 810 811 812 813

}
gboolean
rstto_image_list_iter_previous (RsttoImageListIter *iter)
{
    return iter_previous (iter, TRUE);
814 815
}

816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839

gboolean
rstto_image_list_iter_has_previous (RsttoImageListIter *iter)
{
    RsttoImageList *image_list = iter->priv->image_list;

    if (image_list->priv->wrap_images)
    {
        return TRUE;
    }
    else
    {
        if (rstto_image_list_iter_get_position (iter) == 0)
        {
            return FALSE;
        }
        else
        {
            return TRUE;
        }
    }
}


840 841 842
RsttoImageListIter *
rstto_image_list_iter_clone (RsttoImageListIter *iter)
{
843
    RsttoImageListIter *new_iter = rstto_image_list_iter_new (iter->priv->image_list, iter->priv->file);
Stephan Arts's avatar
Stephan Arts committed
844
    rstto_image_list_iter_set_position (new_iter, rstto_image_list_iter_get_position(iter));
845 846 847

    return new_iter;
}
848 849 850 851

GCompareFunc
rstto_image_list_get_compare_func (RsttoImageList *image_list)
{
Stephan Arts's avatar
Stephan Arts committed
852 853 854
    return (GCompareFunc)image_list->priv->cb_rstto_image_list_compare_func;
}

855 856 857
void
rstto_image_list_set_compare_func (RsttoImageList *image_list, GCompareFunc func)
{
858
    GSList *iter = NULL;
859
    image_list->priv->cb_rstto_image_list_compare_func = func;
860
    image_list->priv->images = g_list_sort (image_list->priv->images,  func);
861

862 863 864 865
    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);
    }
866 867
}

Stephan Arts's avatar
Stephan Arts committed
868 869 870 871
/***********************/
/*  Compare Functions  */
/***********************/

872 873 874
void
rstto_image_list_set_sort_by_name (RsttoImageList *image_list)
{
875
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_image_name_compare_func);
876 877 878 879 880
}

void
rstto_image_list_set_sort_by_date (RsttoImageList *image_list)
{
881
    rstto_image_list_set_compare_func (image_list, (GCompareFunc)cb_rstto_image_list_exif_date_compare_func);
882 883
}

Stephan Arts's avatar
Stephan Arts committed
884 885 886 887 888 889 890 891 892
/**
 * cb_rstto_image_list_image_name_compare_func:
 * @a:
 * @b:
 *
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
893
cb_rstto_image_list_image_name_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
894
{
Stephan Arts's avatar
Stephan Arts committed
895 896
    const gchar *a_base = rstto_file_get_display_name (a);
    const gchar *b_base = rstto_file_get_display_name (b);
897 898 899 900 901
    guint  ac;
    guint  bc;
    const gchar *ap = a_base;
    const gchar *bp = b_base;

Stephan Arts's avatar
Stephan Arts committed
902
    gint result = 0;
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
    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
929

930
    /* If both strings are equal, we're done */
Stephan Arts's avatar
Stephan Arts committed
931 932 933 934 935
    if (ac == bc)
    {
        return 0;
    }
    else
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
    {
        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;
            }
952 953 954 955 956 957 958 959 960 961 962 963 964 965

            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;
            }
966 967
        }
    }
Stephan Arts's avatar
Stephan Arts committed
968

969 970 971 972
    if (result == 0)
    {
        if (ac > bc)
            result = 1;
Stephan Arts's avatar
Stephan Arts committed
973
        if (ac < bc)
974 975 976 977
            result = -1;
    }


Stephan Arts's avatar
Stephan Arts committed
978 979 980 981 982 983 984 985
    return result;
}

/**
 * cb_rstto_image_list_exif_date_compare_func:
 * @a:
 * @b:
 *
986
 * TODO: Use EXIF data if available, not the last-modification-time.
Stephan Arts's avatar
Stephan Arts committed
987 988 989 990
 *
 * Return value: (see strcmp)
 */
static gint
Stephan Arts's avatar
Stephan Arts committed
991
cb_rstto_image_list_exif_date_compare_func (RsttoFile *a, RsttoFile *b)
Stephan Arts's avatar
Stephan Arts committed
992
{
993 994 995 996 997 998 999 1000
    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;
1001
}
1002 1003 1004 1005 1006 1007 1008

gboolean
rstto_image_list_iter_get_sticky (
        RsttoImageListIter *iter)
{
    return iter->priv->sticky;
}
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028

static void
cb_rstto_wrap_images_changed (
        GObject *settings,
        GParamSpec *pspec,
        gpointer user_data)
{
    GValue val_wrap_images = { 0, };

    RsttoImageList *image_list = RSTTO_IMAGE_LIST (user_data);

    g_value_init (&val_wrap_images, G_TYPE_BOOLEAN);

    g_object_get_property (
            settings,
            "wrap-images",
            &val_wrap_images);

    image_list->priv->wrap_images = g_value_get_boolean (&val_wrap_images);
}