~noskcaj/ubuntu/vivid/thunar/1.6.4

« back to all changes in this revision

Viewing changes to thunar/thunar-thumbnailer.c

  • Committer: Bazaar Package Importer
  • Author(s): Lionel Le Folgoc
  • Date: 2010-12-04 16:46:20 UTC
  • mto: (2.1.3 experimental) (1.3.1)
  • mto: This revision was merged to the branch mainline in revision 69.
  • Revision ID: james.westby@ubuntu.com-20101204164620-h7p4t2e9z6hfhz6l
Tags: upstream-1.1.4
ImportĀ upstreamĀ versionĀ 1.1.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vi:set et ai sw=2 sts=2 ts=2: */
 
2
/*-
 
3
 * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or 
 
6
 * modify it under the terms of the GNU General Public License as
 
7
 * published by the Free Software Foundation; either version 2 of 
 
8
 * the License, or (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public 
 
16
 * License along with this program; if not, write to the Free 
 
17
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
18
 * Boston, MA 02110-1301, USA.
 
19
 */
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include <config.h>
 
23
#endif
 
24
 
 
25
#ifdef HAVE_DBUS
 
26
#include <dbus/dbus.h>
 
27
#include <dbus/dbus-glib.h>
 
28
 
 
29
#include <thunar/thunar-thumbnailer-proxy.h>
 
30
#endif
 
31
 
 
32
#include <thunar/thunar-marshal.h>
 
33
#include <thunar/thunar-private.h>
 
34
#include <thunar/thunar-thumbnailer.h>
 
35
 
 
36
 
 
37
 
 
38
/**
 
39
 * WARNING: The source code in this file may do harm to animals. Dead kittens
 
40
 * are to be expected.
 
41
 *
 
42
 *
 
43
 * ThunarThumbnailer is a class for requesting thumbnails from org.xfce.tumbler.*
 
44
 * D-Bus services. This header contains an in-depth description of its design to make
 
45
 * it easier to understand why things are the way they are.
 
46
 *
 
47
 * Please note that all D-Bus calls are performed asynchronously. 
 
48
 *
 
49
 *
 
50
 * Queue
 
51
 * =====
 
52
 *
 
53
 * ThunarThumbnailer maintains a wait queue to group individual thumbnail requests.
 
54
 * The wait queue is processed at most every 100ms. This is done to reduce the
 
55
 * overall D-Bus noise when dealing with a lot of requests. The more thumbnails are 
 
56
 * requested in a 100ms time slot, the less D-Bus methods are sent.
 
57
 *
 
58
 * Let N be the number of requests made for individual files in a 100ms slot. 
 
59
 * Compared to sending out one requests per file (which generates 4*N D-Bus messages,
 
60
 * 1 Queue, 1 Started, 1 Ready/Error and 1 Finished for each of the N files), the wait 
 
61
 * queue technique only causes 3+N D-Bus messages to be sent (1 Queue, 1 Started, 
 
62
 * N Ready/Error and 1 Finished). This can be further improved on the service side
 
63
 * if the D-Bus thumbnailer groups Ready/Error messages (which of course has drawbacks
 
64
 * with regards to the overall thumbnailing responsiveness).
 
65
 *
 
66
 * Before a URI is added to the wait queue, it is checked whether it isn't already
 
67
 * 1) in the wait queue, 2) part of a pending or active request or 3) part of a
 
68
 * finished request which has an idle function pending.
 
69
 *
 
70
 * When a request call is finally sent out, an internal request ID is created and 
 
71
 * associated with the corresponding DBusGProxyCall via the request_call_mapping hash 
 
72
 * table. It also remembers the URIs for the internal request ID in the 
 
73
 * request_uris_mapping hash table. 
 
74
 *
 
75
 * The D-Bus reply handler then checks if there was an delivery error or
 
76
 * not. If the request method was sent successfully, the handle returned by the
 
77
 * D-Bus thumbnailer is associated bidirectionally with the internal request ID via 
 
78
 * the request_handle_mapping and handle_request_mappings. If the request could
 
79
 * not be sent at all, the URIs array is dropped from request_uris_mapping. In 
 
80
 * both cases, the association of the internal request ID with the DBusGProxyCall
 
81
 * is removed from request_call_mapping.
 
82
 *
 
83
 * These hash tables play a major role in the Started, Finished, Error and Ready
 
84
 * signal handlers.
 
85
 *
 
86
 *
 
87
 * Started
 
88
 * =======
 
89
 *
 
90
 * When a Started signal is emitted by the D-Bus thumbnailer, ThunarThumbnailer
 
91
 * receives the handle and looks up the corresponding internal request ID. If
 
92
 * it exists (which it should), it schedules an idle function to handle the
 
93
 * signal in the application's main loop. 
 
94
 *
 
95
 * The idle function then looks up the URIs array for the request ID from the
 
96
 * request_uris_mapping. For each of these URIs the corresponding ThunarFile
 
97
 * is looked up from the file cache (which represents files currently being
 
98
 * used somewhere in the UI), and if the ThunarFile exists in the cache it's
 
99
 * thumb state is set to _LOADING (unless it's already marked as _READY).
 
100
 *
 
101
 *
 
102
 * Ready / Error
 
103
 * =============
 
104
 *
 
105
 * The Ready and Error signal handlers work exactly like Started except that
 
106
 * the Ready idle function sets the thumb state of the corresponding 
 
107
 * ThunarFile objects to _READY and the Error signal sets the state to _NONE.
 
108
 *
 
109
 *
 
110
 * Finished
 
111
 * ========
 
112
 *
 
113
 * The Finished signal handler looks up the internal request ID based on
 
114
 * the D-Bus thumbnailer handle. It then drops all corresponding information
 
115
 * from handle_request_mapping, request_handle_mapping and request_uris_mapping.
 
116
 */
 
117
 
 
118
 
 
119
 
 
120
#if HAVE_DBUS
 
121
typedef enum
 
122
{
 
123
  THUNAR_THUMBNAILER_IDLE_ERROR,
 
124
  THUNAR_THUMBNAILER_IDLE_READY,
 
125
  THUNAR_THUMBNAILER_IDLE_STARTED,
 
126
} ThunarThumbnailerIdleType;
 
127
 
 
128
 
 
129
 
 
130
typedef struct _ThunarThumbnailerCall ThunarThumbnailerCall;
 
131
typedef struct _ThunarThumbnailerIdle ThunarThumbnailerIdle;
 
132
typedef struct _ThunarThumbnailerItem ThunarThumbnailerItem;
 
133
#endif
 
134
 
 
135
 
 
136
 
 
137
static void                   thunar_thumbnailer_finalize               (GObject               *object);
 
138
#ifdef HAVE_DBUS              
 
139
static void                   thunar_thumbnailer_init_thumbnailer_proxy (ThunarThumbnailer     *thumbnailer,
 
140
                                                                         DBusGConnection       *connection);
 
141
static gboolean               thunar_thumbnailer_file_is_supported      (ThunarThumbnailer     *thumbnailer,
 
142
                                                                         ThunarFile            *file);
 
143
static void                   thunar_thumbnailer_thumbnailer_finished   (DBusGProxy            *proxy,
 
144
                                                                         guint                  handle,
 
145
                                                                         ThunarThumbnailer     *thumbnailer);
 
146
static void                   thunar_thumbnailer_thumbnailer_error      (DBusGProxy            *proxy,
 
147
                                                                         guint                  handle,
 
148
                                                                         const gchar          **uris,
 
149
                                                                         gint                   code,
 
150
                                                                         const gchar           *message,
 
151
                                                                         ThunarThumbnailer     *thumbnailer);
 
152
static void                   thunar_thumbnailer_thumbnailer_ready      (DBusGProxy            *proxy,
 
153
                                                                         guint32                handle,
 
154
                                                                         const gchar          **uris,
 
155
                                                                         ThunarThumbnailer     *thumbnailer);
 
156
static void                   thunar_thumbnailer_thumbnailer_started    (DBusGProxy            *proxy,
 
157
                                                                         guint                  handle,
 
158
                                                                         ThunarThumbnailer     *thumbnailer);
 
159
static gpointer               thunar_thumbnailer_queue_async            (ThunarThumbnailer     *thumbnailer,
 
160
                                                                         gchar                **uris,
 
161
                                                                         const gchar          **mime_hints);
 
162
static gboolean               thunar_thumbnailer_error_idle             (gpointer               user_data);
 
163
static gboolean               thunar_thumbnailer_ready_idle             (gpointer               user_data);
 
164
static gboolean               thunar_thumbnailer_started_idle           (gpointer               user_data);
 
165
static void                   thunar_thumbnailer_call_free              (ThunarThumbnailerCall *call);
 
166
static void                   thunar_thumbnailer_idle_free              (gpointer               data);
 
167
static ThunarThumbnailerItem *thunar_thumbnailer_item_new               (GFile                 *file,
 
168
                                                                         const gchar           *mime_hint);
 
169
static void                   thunar_thumbnailer_item_free              (gpointer               data);
 
170
#endif
 
171
 
 
172
 
 
173
 
 
174
struct _ThunarThumbnailerClass
 
175
{
 
176
  GObjectClass __parent__;
 
177
};
 
178
 
 
179
struct _ThunarThumbnailer
 
180
{
 
181
  GObject __parent__;
 
182
 
 
183
#ifdef HAVE_DBUS
 
184
  /* proxies to communicate with D-Bus services */
 
185
  DBusGProxy *thumbnailer_proxy;
 
186
 
 
187
  /* wait queue used to delay (and thereby group) thumbnail requests */
 
188
  GList      *wait_queue;
 
189
  guint       wait_queue_idle_id;
 
190
 
 
191
  /* hash table to map D-Bus service handles to ThunarThumbnailer requests */
 
192
  GHashTable *handle_request_mapping;
 
193
 
 
194
  /* hash table to map ThunarThumbnailer requests to D-Bus service handles */
 
195
  GHashTable *request_handle_mapping;
 
196
 
 
197
  /* hash table to map ThunarThumbnailer requests to DBusGProxyCalls */
 
198
  GHashTable *request_call_mapping;
 
199
 
 
200
  /* hash table to map ThunarThumbnailer requests to URI arrays */
 
201
  GHashTable *request_uris_mapping;
 
202
 
 
203
  GMutex     *lock;
 
204
 
 
205
  /* cached arrays of URI schemes and MIME types for which thumbnails 
 
206
   * can be generated */
 
207
  gchar     **supported_schemes;
 
208
  gchar     **supported_types;
 
209
 
 
210
  /* last ThunarThumbnailer request ID */
 
211
  gpointer    last_request;
 
212
 
 
213
  /* IDs of idle functions */
 
214
  GList      *idles;
 
215
#endif
 
216
};
 
217
 
 
218
#ifdef HAVE_DBUS
 
219
struct _ThunarThumbnailerCall
 
220
{
 
221
  ThunarThumbnailer *thumbnailer;
 
222
  gpointer           request;
 
223
};
 
224
 
 
225
struct _ThunarThumbnailerIdle
 
226
{
 
227
  ThunarThumbnailerIdleType type;
 
228
  ThunarThumbnailer        *thumbnailer;
 
229
  guint                     id;
 
230
 
 
231
  union
 
232
  {
 
233
    char                  **uris;
 
234
    gpointer                request;
 
235
  }                         data;
 
236
};
 
237
 
 
238
struct _ThunarThumbnailerItem
 
239
{
 
240
  GFile *file;
 
241
  gchar *mime_hint;
 
242
};
 
243
#endif
 
244
 
 
245
 
 
246
 
 
247
#ifdef HAVE_DBUS
 
248
static DBusGProxy *thunar_thumbnailer_proxy;
 
249
#endif
 
250
 
 
251
 
 
252
 
 
253
G_DEFINE_TYPE (ThunarThumbnailer, thunar_thumbnailer, G_TYPE_OBJECT);
 
254
 
 
255
 
 
256
 
 
257
static void
 
258
thunar_thumbnailer_class_init (ThunarThumbnailerClass *klass)
 
259
{
 
260
  GObjectClass *gobject_class;
 
261
 
 
262
  gobject_class = G_OBJECT_CLASS (klass);
 
263
  gobject_class->finalize = thunar_thumbnailer_finalize;
 
264
}
 
265
 
 
266
 
 
267
 
 
268
static void
 
269
thunar_thumbnailer_init (ThunarThumbnailer *thumbnailer)
 
270
{
 
271
#ifdef HAVE_DBUS
 
272
  DBusGConnection *connection;
 
273
 
 
274
  thumbnailer->lock = g_mutex_new ();
 
275
  thumbnailer->supported_schemes = NULL;
 
276
  thumbnailer->supported_types = NULL;
 
277
  thumbnailer->last_request = GUINT_TO_POINTER (0);
 
278
  thumbnailer->idles = NULL;
 
279
  thumbnailer->wait_queue_idle_id = 0;
 
280
 
 
281
  /* try to connect to D-Bus */
 
282
  connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
 
283
 
 
284
  /* initialize the proxies */
 
285
  thunar_thumbnailer_init_thumbnailer_proxy (thumbnailer, connection);
 
286
 
 
287
  /* check if we have a thumbnailer proxy */
 
288
  if (thumbnailer->thumbnailer_proxy != NULL)
 
289
    {
 
290
      /* we do, set up the hash tables */
 
291
      thumbnailer->request_handle_mapping = 
 
292
        g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
 
293
      thumbnailer->handle_request_mapping = 
 
294
        g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
 
295
      thumbnailer->request_call_mapping = 
 
296
        g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
 
297
      thumbnailer->request_uris_mapping =
 
298
        g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
 
299
                               (GDestroyNotify) g_strfreev);
 
300
    }
 
301
 
 
302
  /* release the D-Bus connection if we have one */
 
303
  if (connection != NULL)
 
304
    dbus_g_connection_unref (connection);
 
305
#endif
 
306
}
 
307
 
 
308
 
 
309
 
 
310
static void
 
311
thunar_thumbnailer_finalize (GObject *object)
 
312
{
 
313
#ifdef HAVE_DBUS
 
314
  ThunarThumbnailerIdle *idle;
 
315
  ThunarThumbnailer     *thumbnailer = THUNAR_THUMBNAILER (object);
 
316
  GList                 *list;
 
317
  GList                 *lp;
 
318
 
 
319
  /* acquire the thumbnailer lock */
 
320
  g_mutex_lock (thumbnailer->lock);
 
321
 
 
322
  /* clear the request queue */
 
323
  g_list_foreach (thumbnailer->wait_queue, (GFunc) thunar_thumbnailer_item_free, NULL);
 
324
  g_list_free (thumbnailer->wait_queue);
 
325
 
 
326
  /* remove the request queue processing idle handler */
 
327
  if (thumbnailer->wait_queue_idle_id > 0)
 
328
    g_source_remove (thumbnailer->wait_queue_idle_id);
 
329
 
 
330
  /* abort all pending idle functions */
 
331
  for (lp = thumbnailer->idles; lp != NULL; lp = lp->next)
 
332
    {
 
333
      idle = lp->data;
 
334
      g_source_remove (idle->id);
 
335
    }
 
336
 
 
337
  /* free the idle list */
 
338
  g_list_free (thumbnailer->idles);
 
339
 
 
340
  if (thumbnailer->thumbnailer_proxy != NULL)
 
341
    {
 
342
      /* cancel all pending D-Bus calls */
 
343
      list = g_hash_table_get_values (thumbnailer->request_call_mapping);
 
344
      for (lp = list; lp != NULL; lp = lp->next)
 
345
        dbus_g_proxy_cancel_call (thumbnailer->thumbnailer_proxy, lp->data);
 
346
      g_list_free (list);
 
347
 
 
348
      g_hash_table_unref (thumbnailer->request_call_mapping);
 
349
 
 
350
#if 0 
 
351
      /* unqueue all pending requests */
 
352
      list = g_hash_table_get_keys (thumbnailer->handle_request_mapping);
 
353
      for (lp = list; lp != NULL; lp = lp->next)
 
354
        thunar_thumbnailer_unqueue_internal (thumbnailer, GPOINTER_TO_UINT (lp->data));
 
355
      g_list_free (list);
 
356
#endif
 
357
 
 
358
      g_hash_table_unref (thumbnailer->handle_request_mapping);
 
359
      g_hash_table_unref (thumbnailer->request_handle_mapping);
 
360
      g_hash_table_unref (thumbnailer->request_uris_mapping);
 
361
 
 
362
      /* disconnect from the thumbnailer proxy */
 
363
      g_signal_handlers_disconnect_matched (thumbnailer->thumbnailer_proxy,
 
364
                                            G_SIGNAL_MATCH_DATA, 0, 0, 
 
365
                                            NULL, NULL, thumbnailer);
 
366
 
 
367
      /* release the thumbnailer proxy */
 
368
      g_object_unref (thumbnailer->thumbnailer_proxy);
 
369
    }
 
370
 
 
371
  /* free the cached URI schemes and MIME types array */
 
372
  g_strfreev (thumbnailer->supported_schemes);
 
373
  g_strfreev (thumbnailer->supported_types);
 
374
 
 
375
  /* release the thumbnailer lock */
 
376
  g_mutex_unlock (thumbnailer->lock);
 
377
 
 
378
  /* release the mutex */
 
379
  g_mutex_free (thumbnailer->lock);
 
380
#endif
 
381
 
 
382
  (*G_OBJECT_CLASS (thunar_thumbnailer_parent_class)->finalize) (object);
 
383
}
 
384
 
 
385
 
 
386
 
 
387
#ifdef HAVE_DBUS
 
388
static void
 
389
thunar_thumbnailer_init_thumbnailer_proxy (ThunarThumbnailer *thumbnailer,
 
390
                                           DBusGConnection   *connection)
 
391
{
 
392
  /* we can't have a proxy without a D-Bus connection */
 
393
  if (connection == NULL)
 
394
    {
 
395
      thumbnailer->thumbnailer_proxy = NULL;
 
396
      return;
 
397
    }
 
398
 
 
399
  /* create the thumbnailer proxy shared by all ThunarThumbnailers on demand */
 
400
  if (thunar_thumbnailer_proxy == NULL)
 
401
    {
 
402
      /* create the shared thumbnailer proxy */
 
403
      thunar_thumbnailer_proxy = 
 
404
        dbus_g_proxy_new_for_name (connection, 
 
405
                                   "org.freedesktop.thumbnails.Thumbnailer1",
 
406
                                   "/org/freedesktop/thumbnails/Thumbnailer1",
 
407
                                   "org.freedesktop.thumbnails.Thumbnailer1");
 
408
 
 
409
      /* make sure to set it to NULL when the last reference is dropped */
 
410
      g_object_add_weak_pointer (G_OBJECT (thunar_thumbnailer_proxy),
 
411
                                 (gpointer) &thunar_thumbnailer_proxy);
 
412
 
 
413
      thumbnailer->thumbnailer_proxy = thunar_thumbnailer_proxy;
 
414
 
 
415
      /* TODO this should actually be VOID:UINT,BOXED,INT,STRING */
 
416
      dbus_g_object_register_marshaller (_thunar_marshal_VOID__UINT_BOXED_UINT_STRING,
 
417
                                         G_TYPE_NONE,
 
418
                                         G_TYPE_UINT, 
 
419
                                         G_TYPE_STRV, 
 
420
                                         G_TYPE_UINT, 
 
421
                                         G_TYPE_STRING,
 
422
                                         G_TYPE_INVALID);
 
423
 
 
424
      dbus_g_object_register_marshaller ((GClosureMarshal) _thunar_marshal_VOID__UINT_BOXED,
 
425
                                         G_TYPE_NONE,
 
426
                                         G_TYPE_UINT,
 
427
                                         G_TYPE_STRV,
 
428
                                         G_TYPE_INVALID);
 
429
 
 
430
      dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Error", 
 
431
                               G_TYPE_UINT, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_STRING, 
 
432
                               G_TYPE_INVALID);
 
433
      dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Finished", 
 
434
                               G_TYPE_UINT, G_TYPE_INVALID);
 
435
      dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Ready", 
 
436
                               G_TYPE_UINT, G_TYPE_STRV, G_TYPE_INVALID);
 
437
      dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Started", 
 
438
                               G_TYPE_UINT, G_TYPE_INVALID);
 
439
    }
 
440
  else
 
441
    {
 
442
      thumbnailer->thumbnailer_proxy = g_object_ref (thunar_thumbnailer_proxy);
 
443
    }
 
444
 
 
445
  dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Error",
 
446
                               G_CALLBACK (thunar_thumbnailer_thumbnailer_error), 
 
447
                               thumbnailer, NULL);
 
448
  dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Finished",
 
449
                               G_CALLBACK (thunar_thumbnailer_thumbnailer_finished), 
 
450
                               thumbnailer, NULL);
 
451
  dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Ready",
 
452
                               G_CALLBACK (thunar_thumbnailer_thumbnailer_ready), 
 
453
                               thumbnailer, NULL);
 
454
  dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Started", 
 
455
                               G_CALLBACK (thunar_thumbnailer_thumbnailer_started), 
 
456
                               thumbnailer, NULL);
 
457
}
 
458
 
 
459
 
 
460
 
 
461
static gboolean
 
462
thunar_thumbnailer_file_is_supported (ThunarThumbnailer *thumbnailer,
 
463
                                      ThunarFile        *file)
 
464
{
 
465
  const gchar *content_type;
 
466
  gboolean     supported = FALSE;
 
467
  guint        n;
 
468
 
 
469
  _thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
 
470
  _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
 
471
 
 
472
  /* acquire the thumbnailer lock */
 
473
  g_mutex_lock (thumbnailer->lock);
 
474
 
 
475
  /* no types are supported if we don't have a thumbnailer */
 
476
  if (thumbnailer->thumbnailer_proxy == NULL)
 
477
    {
 
478
      /* release the thumbnailer lock */
 
479
      g_mutex_unlock (thumbnailer->lock);
 
480
      return FALSE;
 
481
    }
 
482
 
 
483
  /* request the supported types on demand */
 
484
  if (thumbnailer->supported_schemes == NULL 
 
485
      || thumbnailer->supported_types == NULL)
 
486
    {
 
487
      /* request the supported types from the thumbnailer D-Bus service. We only do
 
488
       * this once, so using a non-async call should be ok */
 
489
      thunar_thumbnailer_proxy_get_supported (thumbnailer->thumbnailer_proxy,
 
490
                                              &thumbnailer->supported_schemes,
 
491
                                              &thumbnailer->supported_types, 
 
492
                                              NULL);
 
493
    }
 
494
 
 
495
  /* check if we have supported URI schemes and MIME types now */
 
496
  if (thumbnailer->supported_schemes != NULL
 
497
      && thumbnailer->supported_types != NULL)
 
498
    {
 
499
      /* determine the content type of the passed file */
 
500
      content_type = thunar_file_get_content_type (file);
 
501
 
 
502
      /* go through all the URI schemes we support */
 
503
      for (n = 0; !supported && thumbnailer->supported_schemes[n] != NULL; ++n)
 
504
        {
 
505
          /* check if the file has the current URI scheme */
 
506
          if (thunar_file_has_uri_scheme (file, thumbnailer->supported_schemes[n]))
 
507
            {
 
508
              /* check if the type of the file is a subtype of the supported type */
 
509
              if (g_content_type_is_a (content_type, thumbnailer->supported_types[n]))
 
510
                supported = TRUE;
 
511
            }
 
512
        }
 
513
    }
 
514
 
 
515
  /* release the thumbnailer lock */
 
516
  g_mutex_unlock (thumbnailer->lock);
 
517
 
 
518
  return supported;
 
519
}
 
520
 
 
521
 
 
522
 
 
523
static void
 
524
thunar_thumbnailer_thumbnailer_error (DBusGProxy        *proxy,
 
525
                                      guint              handle,
 
526
                                      const gchar      **uris,
 
527
                                      gint               code,
 
528
                                      const gchar       *message,
 
529
                                      ThunarThumbnailer *thumbnailer)
 
530
{
 
531
  ThunarThumbnailerIdle *idle;
 
532
  gpointer               request;
 
533
 
 
534
  _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
 
535
  _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
 
536
 
 
537
  /* look up the request ID for this D-Bus service handle */
 
538
  request = g_hash_table_lookup (thumbnailer->handle_request_mapping,
 
539
                                 GUINT_TO_POINTER (handle));
 
540
 
 
541
  /* check if we have a request for this handle */
 
542
  if (request != NULL)
 
543
    {
 
544
      /* allocate a new idle struct */
 
545
      idle = g_slice_new0 (ThunarThumbnailerIdle);
 
546
      idle->type = THUNAR_THUMBNAILER_IDLE_ERROR;
 
547
      idle->thumbnailer = g_object_ref (thumbnailer);
 
548
 
 
549
      /* copy the URIs because we need them in the idle function */
 
550
      idle->data.uris = g_strdupv ((gchar **)uris);
 
551
 
 
552
      /* remember the idle struct because we might have to remove it in finalize() */
 
553
      thumbnailer->idles = g_list_prepend (thumbnailer->idles, idle);
 
554
 
 
555
      /* call the error idle function when we have the time */
 
556
      idle->id = g_idle_add_full (G_PRIORITY_LOW,
 
557
                                  thunar_thumbnailer_error_idle, idle,
 
558
                                  thunar_thumbnailer_idle_free);
 
559
    }
 
560
}
 
561
 
 
562
 
 
563
 
 
564
static void
 
565
thunar_thumbnailer_thumbnailer_finished (DBusGProxy        *proxy,
 
566
                                         guint              handle,
 
567
                                         ThunarThumbnailer *thumbnailer)
 
568
{
 
569
  gpointer request;
 
570
 
 
571
  _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
 
572
  _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
 
573
 
 
574
  /* look up the request ID for this D-Bus service handle */
 
575
  request = g_hash_table_lookup (thumbnailer->handle_request_mapping, 
 
576
                                 GUINT_TO_POINTER (handle));
 
577
 
 
578
  /* check if we have a request for this handle */
 
579
  if (request != NULL)
 
580
    {
 
581
      /* the request is finished, drop all the information about it */
 
582
      g_hash_table_remove (thumbnailer->handle_request_mapping, request);
 
583
      g_hash_table_remove (thumbnailer->request_handle_mapping, request);
 
584
      g_hash_table_remove (thumbnailer->request_uris_mapping, request);
 
585
    }
 
586
}
 
587
 
 
588
 
 
589
 
 
590
static void
 
591
thunar_thumbnailer_thumbnailer_ready (DBusGProxy        *proxy,
 
592
                                      guint32            handle,
 
593
                                      const gchar      **uris,
 
594
                                      ThunarThumbnailer *thumbnailer)
 
595
{
 
596
  ThunarThumbnailerIdle *idle;
 
597
 
 
598
  _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
 
599
  _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
 
600
 
 
601
  /* check if we have any ready URIs */
 
602
  if (uris != NULL)
 
603
    {
 
604
      /* allocate a new idle struct */
 
605
      idle = g_slice_new0 (ThunarThumbnailerIdle);
 
606
      idle->type = THUNAR_THUMBNAILER_IDLE_READY;
 
607
      idle->thumbnailer = g_object_ref (thumbnailer);
 
608
 
 
609
      /* copy the URI array because we need it in the idle function */
 
610
      idle->data.uris = g_strdupv ((gchar **)uris);
 
611
 
 
612
      /* remember the idle struct because we might have to remove it in finalize() */
 
613
      thumbnailer->idles = g_list_prepend (thumbnailer->idles, idle);
 
614
 
 
615
      /* call the ready idle function when we have the time */
 
616
      idle->id = g_idle_add_full (G_PRIORITY_LOW, 
 
617
                                 thunar_thumbnailer_ready_idle, idle, 
 
618
                                 thunar_thumbnailer_idle_free);
 
619
    }
 
620
}
 
621
 
 
622
 
 
623
 
 
624
static void
 
625
thunar_thumbnailer_thumbnailer_started (DBusGProxy        *proxy,
 
626
                                        guint              handle,
 
627
                                        ThunarThumbnailer *thumbnailer)
 
628
{
 
629
  ThunarThumbnailerIdle *idle;
 
630
  gpointer               request;
 
631
 
 
632
  _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
 
633
  _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
 
634
 
 
635
  /* look up the request for this D-Bus service handle */
 
636
  request = g_hash_table_lookup (thumbnailer->handle_request_mapping, 
 
637
                                 GUINT_TO_POINTER (handle));
 
638
 
 
639
  /* check if we have a request for this handle */
 
640
  if (request != NULL)
 
641
    {
 
642
      /* allocate a new idle struct */
 
643
      idle = g_slice_new0 (ThunarThumbnailerIdle);
 
644
      idle->type = THUNAR_THUMBNAILER_IDLE_STARTED;
 
645
      idle->thumbnailer = g_object_ref (thumbnailer);
 
646
 
 
647
      /* remember the request because we need it in the idle function */
 
648
      idle->data.request = request;
 
649
 
 
650
      /* remember the idle struct because we might have to remove it in finalize() */
 
651
      thumbnailer->idles = g_list_prepend (thumbnailer->idles, idle);
 
652
 
 
653
      /* call the started idle function when we have the time */
 
654
      idle->id = g_idle_add_full (G_PRIORITY_LOW,
 
655
                                  thunar_thumbnailer_started_idle, idle, 
 
656
                                  thunar_thumbnailer_idle_free);
 
657
    }
 
658
}
 
659
 
 
660
 
 
661
 
 
662
static void
 
663
thunar_thumbnailer_queue_async_reply (DBusGProxy *proxy,
 
664
                                      guint       handle,
 
665
                                      GError     *error,
 
666
                                      gpointer    user_data)
 
667
{
 
668
  ThunarThumbnailerCall *call = user_data;
 
669
  ThunarThumbnailer     *thumbnailer = THUNAR_THUMBNAILER (call->thumbnailer);
 
670
#ifndef NDEBUG
 
671
  gchar                **uris;
 
672
#endif
 
673
 
 
674
  _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
 
675
  _thunar_return_if_fail (call != NULL);
 
676
  _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
 
677
 
 
678
  g_mutex_lock (thumbnailer->lock);
 
679
 
 
680
  if (error != NULL)
 
681
    {
 
682
#ifndef NDEBUG
 
683
      /* get the URIs array for this request */
 
684
      uris = g_hash_table_lookup (thumbnailer->request_uris_mapping, call->request);
 
685
 
 
686
      /* the array should always exist, otherwise there's a bug in the program */
 
687
      _thunar_assert (uris != NULL);
 
688
#endif
 
689
 
 
690
      /* the request is "finished", forget about its URIs */
 
691
      g_hash_table_remove (thumbnailer->request_uris_mapping, call->request);
 
692
    }
 
693
  else
 
694
    {
 
695
      /* remember that this request and D-Bus handle belong together */
 
696
      g_hash_table_insert (thumbnailer->request_handle_mapping,
 
697
                           call->request, GUINT_TO_POINTER (handle));
 
698
      g_hash_table_insert (thumbnailer->handle_request_mapping, 
 
699
                           GUINT_TO_POINTER (handle), call->request);
 
700
    }
 
701
 
 
702
  /* the queue call is finished, we can forget about its proxy call */
 
703
  g_hash_table_remove (thumbnailer->request_call_mapping, call->request);
 
704
 
 
705
  thunar_thumbnailer_call_free (call);
 
706
 
 
707
  g_mutex_unlock (thumbnailer->lock);
 
708
}
 
709
 
 
710
 
 
711
 
 
712
static gpointer
 
713
thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer,
 
714
                                gchar            **uris,
 
715
                                const gchar      **mime_hints)
 
716
{
 
717
  ThunarThumbnailerCall *thumbnailer_call;
 
718
  DBusGProxyCall        *call;
 
719
  gpointer               request;
 
720
  guint                  request_no;
 
721
 
 
722
  _thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), 0);
 
723
  _thunar_return_val_if_fail (uris != NULL, 0);
 
724
  _thunar_return_val_if_fail (mime_hints != NULL, 0);
 
725
  _thunar_return_val_if_fail (DBUS_IS_G_PROXY (thumbnailer->thumbnailer_proxy), 0);
 
726
 
 
727
  /* compute the next request ID, making sure it's never 0 */
 
728
  request_no = GPOINTER_TO_UINT (thumbnailer->last_request) + 1;
 
729
  request_no = MAX (request_no, 1);
 
730
  
 
731
  /* remember the ID for the next request */
 
732
  thumbnailer->last_request = GUINT_TO_POINTER (request_no);
 
733
 
 
734
  /* use the new request ID for this request */
 
735
  request = thumbnailer->last_request;
 
736
 
 
737
  /* allocate a new call struct for the async D-Bus call */
 
738
  thumbnailer_call = g_slice_new0 (ThunarThumbnailerCall);
 
739
  thumbnailer_call->request = request;
 
740
  thumbnailer_call->thumbnailer = g_object_ref (thumbnailer);
 
741
 
 
742
  /* remember the URIs for this request */
 
743
  g_hash_table_insert (thumbnailer->request_uris_mapping, request, uris);
 
744
 
 
745
  /* queue thumbnails for the given URIs asynchronously */
 
746
  call = thunar_thumbnailer_proxy_queue_async (thumbnailer->thumbnailer_proxy,
 
747
                                               (const gchar **)uris, mime_hints, 
 
748
                                               "normal", "foreground", 0, 
 
749
                                               thunar_thumbnailer_queue_async_reply,
 
750
                                               thumbnailer_call);
 
751
 
 
752
  /* remember to which request the call struct belongs */
 
753
  g_hash_table_insert (thumbnailer->request_call_mapping, request, call);
 
754
 
 
755
  /* return the request ID used for this request */
 
756
  return request;
 
757
}
 
758
 
 
759
 
 
760
 
 
761
static gboolean
 
762
thunar_thumbnailer_error_idle (gpointer user_data)
 
763
{
 
764
  ThunarThumbnailerIdle *idle = user_data;
 
765
  ThunarFile            *file;
 
766
  GFile                 *gfile;
 
767
  guint                  n;
 
768
 
 
769
  _thunar_return_val_if_fail (idle != NULL, FALSE);
 
770
  _thunar_return_val_if_fail (idle->type == THUNAR_THUMBNAILER_IDLE_ERROR, FALSE);
 
771
 
 
772
  /* iterate over all failed URIs */
 
773
  for (n = 0; idle->data.uris != NULL && idle->data.uris[n] != NULL; ++n)
 
774
    {
 
775
      /* look up the corresponding ThunarFile from the cache */
 
776
      gfile = g_file_new_for_uri (idle->data.uris[n]);
 
777
      file = thunar_file_cache_lookup (gfile);
 
778
      g_object_unref (gfile);
 
779
 
 
780
      /* check if we have a file for this URI in the cache */
 
781
      if (file != NULL)
 
782
        {
 
783
          /* set thumbnail state to none unless the thumbnail has already been created.
 
784
           * This is to prevent race conditions with the other idle functions */
 
785
          if (thunar_file_get_thumb_state (file) != THUNAR_FILE_THUMB_STATE_READY)
 
786
            thunar_file_set_thumb_state (file, THUNAR_FILE_THUMB_STATE_NONE);
 
787
        }
 
788
    }
 
789
 
 
790
  /* remove the idle struct */
 
791
  g_mutex_lock (idle->thumbnailer->lock);
 
792
  idle->thumbnailer->idles = g_list_remove (idle->thumbnailer->idles, idle);
 
793
  g_mutex_unlock (idle->thumbnailer->lock);
 
794
 
 
795
  /* remove the idle source, which also destroys the idle struct */
 
796
  return FALSE;
 
797
}
 
798
 
 
799
 
 
800
 
 
801
static gboolean
 
802
thunar_thumbnailer_ready_idle (gpointer user_data)
 
803
{
 
804
  ThunarThumbnailerIdle *idle = user_data;
 
805
  ThunarFile            *file;
 
806
  GFile                 *gfile;
 
807
  guint                  n;
 
808
 
 
809
  _thunar_return_val_if_fail (idle != NULL, FALSE);
 
810
  _thunar_return_val_if_fail (idle->type == THUNAR_THUMBNAILER_IDLE_READY, FALSE);
 
811
 
 
812
  /* iterate over all failed URIs */
 
813
  for (n = 0; idle->data.uris != NULL && idle->data.uris[n] != NULL; ++n)
 
814
    {
 
815
      /* look up the corresponding ThunarFile from the cache */
 
816
      gfile = g_file_new_for_uri (idle->data.uris[n]);
 
817
      file = thunar_file_cache_lookup (gfile);
 
818
      g_object_unref (gfile);
 
819
 
 
820
      /* check if we have a file for this URI in the cache */
 
821
      if (file != NULL)
 
822
        {
 
823
          /* set thumbnail state to ready - we now have a thumbnail */
 
824
          thunar_file_set_thumb_state (file, THUNAR_FILE_THUMB_STATE_READY);
 
825
        }
 
826
    }
 
827
 
 
828
  /* remove the idle struct */
 
829
  g_mutex_lock (idle->thumbnailer->lock);
 
830
  idle->thumbnailer->idles = g_list_remove (idle->thumbnailer->idles, idle);
 
831
  g_mutex_unlock (idle->thumbnailer->lock);
 
832
 
 
833
  /* remove the idle source, which also destroys the idle struct */
 
834
  return FALSE;
 
835
}
 
836
 
 
837
 
 
838
 
 
839
static gboolean
 
840
thunar_thumbnailer_started_idle (gpointer user_data)
 
841
{
 
842
  ThunarThumbnailerIdle *idle = user_data;
 
843
  const gchar          **uris;
 
844
  ThunarFile            *file;
 
845
  GFile                 *gfile;
 
846
  guint                  n;
 
847
 
 
848
  _thunar_return_val_if_fail (idle != NULL, FALSE);
 
849
  _thunar_return_val_if_fail (idle->type == THUNAR_THUMBNAILER_IDLE_STARTED, FALSE);
 
850
 
 
851
  g_mutex_lock (idle->thumbnailer->lock);
 
852
 
 
853
  /* look up the URIs that belong to this request */
 
854
  uris = g_hash_table_lookup (idle->thumbnailer->request_uris_mapping, 
 
855
                              idle->data.request);
 
856
 
 
857
  /* iterate over all URIs if there are any */
 
858
  for (n = 0; uris != NULL && uris[n] != NULL; ++n)
 
859
    {
 
860
      /* look up the corresponding ThunarFile from the cache */
 
861
      gfile = g_file_new_for_uri (uris[n]);
 
862
      file = thunar_file_cache_lookup (gfile);
 
863
      g_object_unref (gfile);
 
864
 
 
865
      /* check if we have a file in the cache */
 
866
      if (file != NULL)
 
867
        {
 
868
          /* set the thumbnail state to loading unless we already have a thumbnail.
 
869
           * This is to prevent race conditions with the other idle functions */
 
870
          if (thunar_file_get_thumb_state (file) != THUNAR_FILE_THUMB_STATE_READY)
 
871
            thunar_file_set_thumb_state (file, THUNAR_FILE_THUMB_STATE_LOADING);
 
872
        }
 
873
    }
 
874
  
 
875
 
 
876
  /* remove the idle struct */
 
877
  idle->thumbnailer->idles = g_list_remove (idle->thumbnailer->idles, idle);
 
878
 
 
879
  g_mutex_unlock (idle->thumbnailer->lock);
 
880
 
 
881
  /* remove the idle source, which also destroys the idle struct */
 
882
  return FALSE;
 
883
}
 
884
 
 
885
 
 
886
 
 
887
static gboolean
 
888
thunar_thumbnailer_file_is_in_wait_queue (ThunarThumbnailer *thumbnailer,
 
889
                                          ThunarFile        *file)
 
890
{
 
891
  ThunarThumbnailerItem *item;
 
892
  gboolean               in_wait_queue = FALSE;
 
893
  GList                 *lp;
 
894
 
 
895
  g_mutex_lock (thumbnailer->lock);
 
896
 
 
897
  for (lp = thumbnailer->wait_queue; !in_wait_queue && lp != NULL; lp = lp->next)
 
898
    {
 
899
      item = lp->data;
 
900
 
 
901
      if (g_file_equal (item->file, thunar_file_get_file (file)))
 
902
        in_wait_queue = TRUE;
 
903
    }
 
904
 
 
905
  g_mutex_unlock (thumbnailer->lock);
 
906
 
 
907
  return in_wait_queue;
 
908
}
 
909
 
 
910
 
 
911
 
 
912
static gboolean
 
913
thunar_thumbnailer_process_wait_queue (ThunarThumbnailer *thumbnailer)
 
914
{
 
915
  ThunarThumbnailerItem *item;
 
916
  gpointer               request;
 
917
  GList                 *lp;
 
918
  gchar                **mime_hints;
 
919
  gchar                **uris;
 
920
  guint                  n_items;
 
921
  guint                  n;
 
922
 
 
923
  _thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
 
924
 
 
925
  g_mutex_lock (thumbnailer->lock);
 
926
 
 
927
  /* determine how many URIs are in the wait queue */
 
928
  n_items = g_list_length (thumbnailer->wait_queue);
 
929
 
 
930
  /* allocate arrays for URIs and mime hints */
 
931
  uris = g_new0 (gchar *, n_items + 1);
 
932
  mime_hints = g_new0 (gchar *, n_items + 1);
 
933
 
 
934
  /* fill URI and MIME hint arrays with items from the wait queue */
 
935
  for (lp = g_list_last (thumbnailer->wait_queue), n = 0; lp != NULL; lp = lp->prev, ++n)
 
936
    {
 
937
      /* fetch the next item from the queue */
 
938
      item = lp->data;
 
939
 
 
940
      /* save URI and MIME hint in the arrays */
 
941
      uris[n] = g_file_get_uri (item->file);
 
942
      mime_hints[n] = item->mime_hint;
 
943
 
 
944
      /* destroy the GFile and the queue item. The MIME hints are free'd later */
 
945
      g_object_unref (item->file);
 
946
      g_slice_free (ThunarThumbnailerItem, item);
 
947
    }
 
948
 
 
949
  /* NULL-terminate both arrays */
 
950
  uris[n] = NULL;
 
951
  mime_hints[n] = NULL;
 
952
 
 
953
  /* queue a thumbnail request for the URIs from the wait queue */
 
954
  request = thunar_thumbnailer_queue_async (thumbnailer, uris, 
 
955
                                            (const gchar **)mime_hints);
 
956
 
 
957
  /* free mime hints array */
 
958
  g_strfreev (mime_hints);
 
959
 
 
960
  /* clear the wait queue */
 
961
  g_list_free (thumbnailer->wait_queue);
 
962
  thumbnailer->wait_queue = NULL;
 
963
 
 
964
  /* reset the wait queue idle ID */
 
965
  thumbnailer->wait_queue_idle_id = 0;
 
966
 
 
967
  g_mutex_unlock (thumbnailer->lock);
 
968
 
 
969
  return FALSE;
 
970
}
 
971
 
 
972
 
 
973
 
 
974
static gboolean
 
975
thunar_thumbnailer_file_is_queued (ThunarThumbnailer *thumbnailer,
 
976
                                   ThunarFile        *file)
 
977
{
 
978
  gboolean is_queued = FALSE;
 
979
  GList   *values;
 
980
  GList   *lp;
 
981
  gchar  **uris;
 
982
  gchar   *uri;
 
983
  guint    n;
 
984
 
 
985
  /* get a list with all URI arrays of already queued requests */
 
986
  values = g_hash_table_get_values (thumbnailer->request_uris_mapping);
 
987
 
 
988
  /* if we have none, the file cannot be queued ... or can it? ;) */
 
989
  if (values == NULL)
 
990
    return FALSE;
 
991
 
 
992
  /* determine the URI for this file */
 
993
  uri = thunar_file_dup_uri (file);
 
994
 
 
995
  /* iterate over all URI arrays */
 
996
  for (lp = values; !is_queued && lp != NULL; lp = lp->next)
 
997
    {
 
998
      uris = lp->data;
 
999
 
 
1000
      /* check if the file is included in the URI array of the current request */
 
1001
      for (n = 0; !is_queued && uris != NULL && uris[n] != NULL; ++n)
 
1002
        if (g_utf8_collate (uri, uris[n]) == 0)
 
1003
          is_queued = TRUE;
 
1004
    }
 
1005
 
 
1006
  /* free the file URI */
 
1007
  g_free (uri);
 
1008
 
 
1009
  /* free the URI array list */
 
1010
  g_list_free (values);
 
1011
 
 
1012
  return is_queued;
 
1013
}
 
1014
 
 
1015
 
 
1016
 
 
1017
static gboolean
 
1018
thunar_thumbnailer_file_is_ready (ThunarThumbnailer *thumbnailer,
 
1019
                                  ThunarFile        *file)
 
1020
{
 
1021
  ThunarThumbnailerIdle *idle;
 
1022
  gboolean               is_ready = FALSE;
 
1023
  GList                 *lp;
 
1024
  gchar                 *uri;
 
1025
  guint                  n;
 
1026
 
 
1027
  /* determine the URI or this file */
 
1028
  uri = thunar_file_dup_uri (file);
 
1029
 
 
1030
  /* iterate over all idle structs */
 
1031
  for (lp = thumbnailer->idles; !is_ready && lp != NULL; lp = lp->next)
 
1032
    {
 
1033
      /* skip invalid idles */
 
1034
      if (lp->data != NULL)
 
1035
        continue;
 
1036
 
 
1037
      idle = lp->data;
 
1038
 
 
1039
      /* skip non-ready idles and idles without any URIs */
 
1040
      if (idle->type != THUNAR_THUMBNAILER_IDLE_READY || idle->data.uris == NULL)
 
1041
        continue;
 
1042
 
 
1043
      /* check if the file is included in this ready idle */
 
1044
      for (n = 0; !is_ready && idle->data.uris[n] != NULL; ++n)
 
1045
        if (g_utf8_collate (uri, idle->data.uris[n]) == 0)
 
1046
          is_ready = TRUE;
 
1047
    }
 
1048
 
 
1049
  /* free the file URI */
 
1050
  g_free (uri);
 
1051
 
 
1052
  return is_ready;
 
1053
}
 
1054
 
 
1055
 
 
1056
 
 
1057
static void
 
1058
thunar_thumbnailer_call_free (ThunarThumbnailerCall *call)
 
1059
{
 
1060
  _thunar_return_if_fail (call != NULL);
 
1061
 
 
1062
  /* drop the thumbnailer reference */
 
1063
  g_object_unref (call->thumbnailer);
 
1064
 
 
1065
  /* free the struct */
 
1066
  g_slice_free (ThunarThumbnailerCall, call);
 
1067
}
 
1068
 
 
1069
 
 
1070
 
 
1071
static void
 
1072
thunar_thumbnailer_idle_free (gpointer data)
 
1073
{
 
1074
  ThunarThumbnailerIdle *idle = data;
 
1075
 
 
1076
  _thunar_return_if_fail (idle != NULL);
 
1077
 
 
1078
  /* free the URI array if necessary */
 
1079
  if (idle->type == THUNAR_THUMBNAILER_IDLE_READY 
 
1080
      || idle->type == THUNAR_THUMBNAILER_IDLE_ERROR)
 
1081
    {
 
1082
      g_strfreev (idle->data.uris);
 
1083
    }
 
1084
 
 
1085
  /* drop the thumbnailer reference */
 
1086
  g_object_unref (idle->thumbnailer);
 
1087
 
 
1088
  /* free the struct */
 
1089
  g_slice_free (ThunarThumbnailerIdle, idle);
 
1090
}
 
1091
 
 
1092
 
 
1093
 
 
1094
static ThunarThumbnailerItem *
 
1095
thunar_thumbnailer_item_new (GFile       *file,
 
1096
                             const gchar *mime_hint)
 
1097
{
 
1098
  ThunarThumbnailerItem *item;
 
1099
 
 
1100
  _thunar_return_val_if_fail (G_IS_FILE (file), NULL);
 
1101
  _thunar_return_val_if_fail (mime_hint != NULL && mime_hint != '\0', NULL);
 
1102
 
 
1103
  item = g_slice_new0 (ThunarThumbnailerItem);
 
1104
  item->file = g_object_ref (file);
 
1105
  item->mime_hint = g_strdup (mime_hint);
 
1106
 
 
1107
  return item;
 
1108
}
 
1109
 
 
1110
 
 
1111
static void
 
1112
thunar_thumbnailer_item_free (gpointer data)
 
1113
{
 
1114
  ThunarThumbnailerItem *item = data;
 
1115
 
 
1116
  g_object_unref (item->file);
 
1117
  g_free (item->mime_hint);
 
1118
  g_slice_free (ThunarThumbnailerItem, item);
 
1119
}
 
1120
#endif /* HAVE_DBUS */
 
1121
 
 
1122
 
 
1123
 
 
1124
/**
 
1125
 * thunar_thumbnailer_new:
 
1126
 *
 
1127
 * Allocates a new #ThunarThumbnailer object, which can be used to 
 
1128
 * generate and store thumbnails for files.
 
1129
 *
 
1130
 * The caller is responsible to free the returned
 
1131
 * object using g_object_unref() when no longer needed.
 
1132
 *
 
1133
 * Return value: a newly allocated #ThunarThumbnailer.
 
1134
 **/
 
1135
ThunarThumbnailer*
 
1136
thunar_thumbnailer_new (void)
 
1137
{
 
1138
  return g_object_new (THUNAR_TYPE_THUMBNAILER, NULL);
 
1139
}
 
1140
 
 
1141
 
 
1142
 
 
1143
gboolean
 
1144
thunar_thumbnailer_queue_file (ThunarThumbnailer *thumbnailer,
 
1145
                               ThunarFile        *file)
 
1146
{
 
1147
  GList files;
 
1148
 
 
1149
  _thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
 
1150
  _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
 
1151
 
 
1152
  /* fake a file list */
 
1153
  files.data = file;
 
1154
  files.next = NULL;
 
1155
  files.prev = NULL;
 
1156
 
 
1157
  /* queue a thumbnail request for the file */
 
1158
  return thunar_thumbnailer_queue_files (thumbnailer, &files);
 
1159
}
 
1160
 
 
1161
 
 
1162
 
 
1163
gboolean
 
1164
thunar_thumbnailer_queue_files (ThunarThumbnailer *thumbnailer,
 
1165
                                GList             *files)
 
1166
{
 
1167
#ifdef HAVE_DBUS
 
1168
  ThunarThumbnailerItem *item;
 
1169
#endif
 
1170
  gboolean               success = FALSE;
 
1171
#ifdef HAVE_DBUS
 
1172
  GList                 *lp;
 
1173
  GList                 *supported_files = NULL;
 
1174
#endif
 
1175
 
 
1176
  _thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
 
1177
  _thunar_return_val_if_fail (files != NULL, FALSE);
 
1178
 
 
1179
#ifdef HAVE_DBUS
 
1180
  /* acquire the thumbnailer lock */
 
1181
  g_mutex_lock (thumbnailer->lock);
 
1182
 
 
1183
  if (thumbnailer->thumbnailer_proxy == NULL)
 
1184
    {
 
1185
      /* release the lock and abort */
 
1186
      g_mutex_unlock (thumbnailer->lock);
 
1187
      return FALSE;
 
1188
    }
 
1189
 
 
1190
  /* release the thumbnailer lock */
 
1191
  g_mutex_unlock (thumbnailer->lock);
 
1192
 
 
1193
  /* collect all supported files from the list that are neither in the
 
1194
   * about to be queued (wait queue), nor already queued, nor already 
 
1195
   * processed (and awaiting to be refreshed) */
 
1196
  for (lp = g_list_last (files); lp != NULL; lp = lp->prev)
 
1197
    if (thunar_thumbnailer_file_is_supported (thumbnailer, lp->data))
 
1198
      {
 
1199
        if (!thunar_thumbnailer_file_is_in_wait_queue (thumbnailer, lp->data)
 
1200
            && !thunar_thumbnailer_file_is_queued (thumbnailer, lp->data)
 
1201
            && !thunar_thumbnailer_file_is_ready (thumbnailer, lp->data))
 
1202
          {
 
1203
            supported_files = g_list_prepend (supported_files, lp->data);
 
1204
          }
 
1205
      }
 
1206
 
 
1207
  /* check if we have any supported files */
 
1208
  if (supported_files != NULL)
 
1209
    {
 
1210
      for (lp = supported_files; lp != NULL; lp = lp->next)
 
1211
        {
 
1212
          g_mutex_lock (thumbnailer->lock);
 
1213
 
 
1214
          /* allocate a thumbnailer item for the wait queue */
 
1215
          item = thunar_thumbnailer_item_new (thunar_file_get_file (lp->data),
 
1216
                                              thunar_file_get_content_type (lp->data));
 
1217
 
 
1218
          /* add the item to the wait queue */
 
1219
          thumbnailer->wait_queue = g_list_prepend (thumbnailer->wait_queue, item);
 
1220
 
 
1221
          g_mutex_unlock (thumbnailer->lock);
 
1222
        }
 
1223
 
 
1224
      g_mutex_lock (thumbnailer->lock);
 
1225
 
 
1226
      if (thumbnailer->wait_queue_idle_id == 0)
 
1227
        {
 
1228
          thumbnailer->wait_queue_idle_id = 
 
1229
            g_timeout_add (100, (GSourceFunc) thunar_thumbnailer_process_wait_queue, 
 
1230
                           thumbnailer);
 
1231
        }
 
1232
 
 
1233
      g_mutex_unlock (thumbnailer->lock);
 
1234
      
 
1235
      /* free the list of supported files */
 
1236
      g_list_free (supported_files);
 
1237
 
 
1238
      /* we assume success if we've come so far */
 
1239
      success = TRUE;
 
1240
    }
 
1241
#endif /* HAVE_DBUS */
 
1242
 
 
1243
  return success;
 
1244
}
 
1245
 
 
1246
 
 
1247
#if 0 
 
1248
static void
 
1249
thunar_thumbnailer_unqueue (ThunarThumbnailer *thumbnailer,
 
1250
                            gpointer           request)
 
1251
{
 
1252
#ifdef HAVE_DBUS
 
1253
  gpointer handle;
 
1254
#endif
 
1255
 
 
1256
  _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
 
1257
 
 
1258
#ifdef HAVE_DBUS
 
1259
  /* acquire the thumbnailer lock */
 
1260
  g_mutex_lock (thumbnailer->lock);
 
1261
 
 
1262
  if (thumbnailer->thumbnailer_proxy != NULL)
 
1263
    {
 
1264
      handle = g_hash_table_lookup (thumbnailer->request_handle_mapping, request);
 
1265
 
 
1266
      thunar_thumbnailer_proxy_unqueue (thumbnailer->thumbnailer_proxy, 
 
1267
                                        GPOINTER_TO_UINT (handle), NULL);
 
1268
 
 
1269
      g_hash_table_remove (thumbnailer->handle_request_mapping, handle);
 
1270
      g_hash_table_remove (thumbnailer->request_handle_mapping, request);
 
1271
      g_hash_table_remove (thumbnailer->request_uris_mapping, request);
 
1272
    }
 
1273
 
 
1274
  /* release the thumbnailer lock */
 
1275
  g_mutex_unlock (thumbnailer->lock);
 
1276
#endif
 
1277
}
 
1278
#endif