Bug Summary

File:ctk/ctkrecentmanager.c
Warning:line 2311, column 18
This statement is never executed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ctkrecentmanager.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/ctk -fcoverage-compilation-dir=/rootdir/ctk -resource-dir /usr/lib/llvm-21/lib/clang/21 -D HAVE_CONFIG_H -I . -I .. -D G_LOG_DOMAIN="Ctk" -D G_LOG_USE_STRUCTURED=1 -D CTK_VERSION="3.25.7" -D CTK_BINARY_VERSION="3.0.0" -D CTK_COMPILATION -D CTK_PRINT_BACKEND_ENABLE_UNSUPPORTED -D CTK_LIBDIR="/usr/lib" -D CTK_LOCALEDIR="/usr/share/locale" -D CTK_DATADIR="/usr/share" -D CTK_DATA_PREFIX="/usr" -D CTK_SYSCONFDIR="/usr/etc" -D CTK_HOST="x86_64-pc-linux-gnu" -D CTK_PRINT_BACKENDS="file,cups" -D X11_DATA_PREFIX="/usr" -D ISO_CODES_PREFIX="" -I .. -I ../ctk -I .. -I ../cdk -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D G_ENABLE_DEBUG -D G_ENABLE_CONSISTENCY_CHECKS -D GLIB_MIN_REQUIRED_VERSION=GLIB_VERSION_2_66 -D GLIB_MAX_ALLOWED_VERSION=GLIB_VERSION_2_66 -I /usr/include/pango-1.0 -I /usr/include/cairo -I /usr/local/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/atk-1.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/fribidi -I /usr/include/pixman-1 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -I /usr/include/pango-1.0 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D PIC -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-checker deadcode.DeadStores -analyzer-checker security.ArrayBound -analyzer-checker unix.cstring.NotNullTerminated -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2026-05-20-082140-45483-1 -x c ctkrecentmanager.c
1/* CTK - The GIMP Toolkit
2 * ctkrecentmanager.c: a manager for the recently used resources
3 *
4 * Copyright (C) 2006 Emmanuele Bassi
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/**
21 * SECTION:ctkrecentmanager
22 * @Title: CtkRecentManager
23 * @short_description: Managing recently used files
24 * @See_Also: #GBookmarkFile, #CtkSettings, #CtkRecentChooser
25 *
26 * #CtkRecentManager provides a facility for adding, removing and
27 * looking up recently used files. Each recently used file is
28 * identified by its URI, and has meta-data associated to it, like
29 * the names and command lines of the applications that have
30 * registered it, the number of time each application has registered
31 * the same file, the mime type of the file and whether the file
32 * should be displayed only by the applications that have
33 * registered it.
34 *
35 * The recently used files list is per user.
36 *
37 * The #CtkRecentManager acts like a database of all the recently
38 * used files. You can create new #CtkRecentManager objects, but
39 * it is more efficient to use the default manager created by CTK+.
40 *
41 * Adding a new recently used file is as simple as:
42 *
43 * |[<!-- language="C" -->
44 * CtkRecentManager *manager;
45 *
46 * manager = ctk_recent_manager_get_default ();
47 * ctk_recent_manager_add_item (manager, file_uri);
48 * ]|
49 *
50 * The #CtkRecentManager will try to gather all the needed information
51 * from the file itself through GIO.
52 *
53 * Looking up the meta-data associated with a recently used file
54 * given its URI requires calling ctk_recent_manager_lookup_item():
55 *
56 * |[<!-- language="C" -->
57 * CtkRecentManager *manager;
58 * CtkRecentInfo *info;
59 * GError *error = NULL;
60 *
61 * manager = ctk_recent_manager_get_default ();
62 * info = ctk_recent_manager_lookup_item (manager, file_uri, &error);
63 * if (error)
64 * {
65 * g_warning ("Could not find the file: %s", error->message);
66 * g_error_free (error);
67 * }
68 * else
69 * {
70 * // Use the info object
71 * ctk_recent_info_unref (info);
72 * }
73 * ]|
74 *
75 * In order to retrieve the list of recently used files, you can use
76 * ctk_recent_manager_get_items(), which returns a list of #CtkRecentInfo-structs.
77 *
78 * A #CtkRecentManager is the model used to populate the contents of
79 * one, or more #CtkRecentChooser implementations.
80 *
81 * Note that the maximum age of the recently used files list is
82 * controllable through the #CtkSettings:ctk-recent-files-max-age
83 * property.
84 *
85 * Recently used files are supported since CTK+ 2.10.
86 */
87
88#include "config.h"
89
90#include <sys/types.h>
91#include <sys/stat.h>
92#ifdef HAVE_UNISTD_H1
93#include <unistd.h>
94#endif
95#include <errno(*__errno_location ()).h>
96#include <string.h>
97#include <stdlib.h>
98#include <glib.h>
99#include <glib/gstdio.h>
100#include <gio/gio.h>
101
102#include "ctkrecentmanager.h"
103#include "ctkintl.h"
104#include "ctksettings.h"
105#include "ctkicontheme.h"
106#include "ctktypebuiltins.h"
107#include "ctkprivate.h"
108#include "ctkmarshalers.h"
109
110/* the file where we store the recently used items */
111#define CTK_RECENTLY_USED_FILE"recently-used.xbel" "recently-used.xbel"
112
113/* return all items by default */
114#define DEFAULT_LIMIT-1 -1
115
116/* limit the size of the list */
117#define MAX_LIST_SIZE1000 1000
118
119/* keep in sync with xdgmime */
120#define CTK_RECENT_DEFAULT_MIME"application/octet-stream" "application/octet-stream"
121
122typedef struct
123{
124 gchar *name;
125 gchar *exec;
126
127 guint count;
128
129 GDateTime *stamp;
130} RecentAppInfo;
131
132/**
133 * CtkRecentInfo:
134 *
135 * #CtkRecentInfo-struct contains private data only, and should
136 * be accessed using the provided API.
137 *
138 * #CtkRecentInfo constains all the meta-data
139 * associated with an entry in the recently used files list.
140 *
141 * Since: 2.10
142 */
143struct _CtkRecentInfo
144{
145 gchar *uri;
146
147 gchar *display_name;
148 gchar *description;
149
150 GDateTime *added;
151 GDateTime *modified;
152 GDateTime *visited;
153
154 gchar *mime_type;
155
156 RecentAppInfo *applications;
157 int n_applications;
158 GHashTable *apps_lookup;
159
160 char **groups;
161 int n_groups;
162
163 gboolean is_private;
164
165 GdkPixbuf *icon;
166
167 gint ref_count;
168};
169
170struct _CtkRecentManagerPrivate
171{
172 gchar *filename;
173
174 guint is_dirty : 1;
175
176 gint size;
177
178 GBookmarkFile *recent_items;
179
180 GFileMonitor *monitor;
181
182 guint changed_timeout;
183 guint changed_age;
184};
185
186enum
187{
188 PROP_0,
189
190 PROP_FILENAME,
191 PROP_LIMIT,
192 PROP_SIZE
193};
194
195static void ctk_recent_manager_dispose (GObject *object);
196static void ctk_recent_manager_finalize (GObject *object);
197static void ctk_recent_manager_set_property (GObject *object,
198 guint prop_id,
199 const GValue *value,
200 GParamSpec *pspec);
201static void ctk_recent_manager_get_property (GObject *object,
202 guint prop_id,
203 GValue *value,
204 GParamSpec *pspec);
205static void ctk_recent_manager_add_item_query_info (GObject *source_object,
206 GAsyncResult *res,
207 gpointer user_data);
208static void ctk_recent_manager_monitor_changed (GFileMonitor *monitor,
209 GFile *file,
210 GFile *other_file,
211 GFileMonitorEvent event_type,
212 gpointer user_data);
213static void ctk_recent_manager_changed (CtkRecentManager *manager);
214static void ctk_recent_manager_real_changed (CtkRecentManager *manager);
215static void ctk_recent_manager_set_filename (CtkRecentManager *manager,
216 const gchar *filename);
217static void ctk_recent_manager_clamp_to_age (CtkRecentManager *manager,
218 gint age);
219static void ctk_recent_manager_clamp_to_size (CtkRecentManager *manager,
220 const gint size);
221
222static void ctk_recent_manager_enabled_changed (CtkRecentManager *manager);
223
224
225static void build_recent_items_list (CtkRecentManager *manager);
226static void purge_recent_items_list (CtkRecentManager *manager,
227 GError **error);
228
229static CtkRecentInfo *ctk_recent_info_new (const gchar *uri);
230static void ctk_recent_info_free (CtkRecentInfo *recent_info);
231
232static guint signal_changed = 0;
233
234static CtkRecentManager *recent_manager_singleton = NULL((void*)0);
235
236G_DEFINE_TYPE_WITH_PRIVATE (CtkRecentManager, ctk_recent_manager, G_TYPE_OBJECT)static void ctk_recent_manager_init (CtkRecentManager *self);
static void ctk_recent_manager_class_init (CtkRecentManagerClass
*klass); static GType ctk_recent_manager_get_type_once (void
); static gpointer ctk_recent_manager_parent_class = ((void*)
0); static gint CtkRecentManager_private_offset; static void ctk_recent_manager_class_intern_init
(gpointer klass) { ctk_recent_manager_parent_class = g_type_class_peek_parent
(klass); if (CtkRecentManager_private_offset != 0) g_type_class_adjust_private_offset
(klass, &CtkRecentManager_private_offset); ctk_recent_manager_class_init
((CtkRecentManagerClass*) klass); } __attribute__ ((__unused__
)) static inline gpointer ctk_recent_manager_get_instance_private
(CtkRecentManager *self) { return (((gpointer) ((guint8*) (self
) + (glong) (CtkRecentManager_private_offset)))); } GType ctk_recent_manager_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_recent_manager_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_recent_manager_get_type_once
(void) { GType g_define_type_id = g_type_register_static_simple
(((GType) ((20) << (2))), g_intern_static_string ("CtkRecentManager"
), sizeof (CtkRecentManagerClass), (GClassInitFunc)(void (*)(
void)) ctk_recent_manager_class_intern_init, sizeof (CtkRecentManager
), (GInstanceInitFunc)(void (*)(void)) ctk_recent_manager_init
, (GTypeFlags) 0); { {{ CtkRecentManager_private_offset = g_type_add_instance_private
(g_define_type_id, sizeof (CtkRecentManagerPrivate)); };} } return
g_define_type_id; }
237
238/* Test of haystack has the needle prefix, comparing case
239 * insensitive. haystack may be UTF-8, but needle must
240 * contain only lowercase ascii.
241 */
242static gboolean
243has_case_prefix (const gchar *haystack,
244 const gchar *needle)
245{
246 const gchar *h, *n;
247
248 /* Eat one character at a time. */
249 h = haystack;
250 n = needle;
251
252 while (*n && *h && *n == g_ascii_tolower (*h))
253 {
254 n++;
255 h++;
256 }
257
258 return *n == '\0';
259}
260
261GQuark
262ctk_recent_manager_error_quark (void)
263{
264 return g_quark_from_static_string ("ctk-recent-manager-error-quark");
265}
266
267static void
268ctk_recent_manager_class_init (CtkRecentManagerClass *klass)
269{
270 GObjectClass *gobject_class = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((klass)), (((GType) ((20) << (2))))))))
;
271
272 gobject_class->set_property = ctk_recent_manager_set_property;
273 gobject_class->get_property = ctk_recent_manager_get_property;
274 gobject_class->dispose = ctk_recent_manager_dispose;
275 gobject_class->finalize = ctk_recent_manager_finalize;
276
277 /**
278 * CtkRecentManager:filename:
279 *
280 * The full path to the file to be used to store and read the
281 * recently used resources list
282 *
283 * Since: 2.10
284 */
285 g_object_class_install_property (gobject_class,
286 PROP_FILENAME,
287 g_param_spec_string ("filename",
288 P_("Filename")g_dgettext("ctk30" "-properties","Filename"),
289 P_("The full path to the file to be used to store and read the list")g_dgettext("ctk30" "-properties","The full path to the file to be used to store and read the list"
)
,
290 NULL((void*)0),
291 (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)));
292
293 /**
294 * CtkRecentManager:size:
295 *
296 * The size of the recently used resources list.
297 *
298 * Since: 2.10
299 */
300 g_object_class_install_property (gobject_class,
301 PROP_SIZE,
302 g_param_spec_int ("size",
303 P_("Size")g_dgettext("ctk30" "-properties","Size"),
304 P_("The size of the recently used resources list")g_dgettext("ctk30" "-properties","The size of the recently used resources list"
)
,
305 -1,
306 G_MAXINT2147483647,
307 0,
308 G_PARAM_READABLE));
309
310 /**
311 * CtkRecentManager::changed:
312 * @recent_manager: the recent manager
313 *
314 * Emitted when the current recently used resources manager changes
315 * its contents, either by calling ctk_recent_manager_add_item() or
316 * by another application.
317 *
318 * Since: 2.10
319 */
320 signal_changed =
321 g_signal_new (I_("changed")g_intern_static_string ("changed"),
322 G_TYPE_FROM_CLASS (klass)(((GTypeClass*) (klass))->g_type),
323 G_SIGNAL_RUN_FIRST,
324 G_STRUCT_OFFSET (CtkRecentManagerClass, changed)((glong) __builtin_offsetof(CtkRecentManagerClass, changed)),
325 NULL((void*)0), NULL((void*)0),
326 NULL((void*)0),
327 G_TYPE_NONE((GType) ((1) << (2))), 0);
328
329 klass->changed = ctk_recent_manager_real_changed;
330}
331
332static void
333ctk_recent_manager_init (CtkRecentManager *manager)
334{
335 CtkRecentManagerPrivate *priv;
336 CtkSettings *settings;
337
338 manager->priv = ctk_recent_manager_get_instance_private (manager);
339 priv = manager->priv;
340
341 priv->size = 0;
342 priv->filename = NULL((void*)0);
343
344 settings = ctk_settings_get_default ();
345 if (settings)
346 g_signal_connect_swapped (settings, "notify::ctk-recent-files-enabled",g_signal_connect_data ((settings), ("notify::ctk-recent-files-enabled"
), (((GCallback) (ctk_recent_manager_enabled_changed))), (manager
), ((void*)0), G_CONNECT_SWAPPED)
347 G_CALLBACK (ctk_recent_manager_enabled_changed), manager)g_signal_connect_data ((settings), ("notify::ctk-recent-files-enabled"
), (((GCallback) (ctk_recent_manager_enabled_changed))), (manager
), ((void*)0), G_CONNECT_SWAPPED)
;
348}
349
350static void
351ctk_recent_manager_set_property (GObject *object,
352 guint prop_id,
353 const GValue *value,
354 GParamSpec *pspec)
355{
356 CtkRecentManager *recent_manager = CTK_RECENT_MANAGER (object)((((CtkRecentManager*) (void *) g_type_check_instance_cast ((
GTypeInstance*) ((object)), ((ctk_recent_manager_get_type ())
)))))
;
357
358 switch (prop_id)
359 {
360 case PROP_FILENAME:
361 ctk_recent_manager_set_filename (recent_manager, g_value_get_string (value));
362 break;
363 default:
364 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "ctkrecentmanager.c", 364, ("property"), _glib__property_id
, _glib__pspec->name, g_type_name ((((((GTypeClass*) (((GTypeInstance
*) (_glib__pspec))->g_class))->g_type)))), (g_type_name
((((((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
365 break;
366 }
367}
368
369static void
370ctk_recent_manager_get_property (GObject *object,
371 guint prop_id,
372 GValue *value,
373 GParamSpec *pspec)
374{
375 CtkRecentManager *recent_manager = CTK_RECENT_MANAGER (object)((((CtkRecentManager*) (void *) g_type_check_instance_cast ((
GTypeInstance*) ((object)), ((ctk_recent_manager_get_type ())
)))))
;
376
377 switch (prop_id)
378 {
379 case PROP_FILENAME:
380 g_value_set_string (value, recent_manager->priv->filename);
381 break;
382 case PROP_SIZE:
383 g_value_set_int (value, recent_manager->priv->size);
384 break;
385 default:
386 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "ctkrecentmanager.c", 386, ("property"), _glib__property_id
, _glib__pspec->name, g_type_name ((((((GTypeClass*) (((GTypeInstance
*) (_glib__pspec))->g_class))->g_type)))), (g_type_name
((((((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
387 break;
388 }
389}
390
391static void
392ctk_recent_manager_finalize (GObject *object)
393{
394 CtkRecentManager *manager = CTK_RECENT_MANAGER (object)((((CtkRecentManager*) (void *) g_type_check_instance_cast ((
GTypeInstance*) ((object)), ((ctk_recent_manager_get_type ())
)))))
;
395 CtkRecentManagerPrivate *priv = manager->priv;
396
397 g_free (priv->filename);
398
399 if (priv->recent_items != NULL((void*)0))
400 g_bookmark_file_free (priv->recent_items);
401
402 G_OBJECT_CLASS (ctk_recent_manager_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_recent_manager_parent_class)), (((GType) ((20) <<
(2))))))))
->finalize (object);
403}
404
405static void
406ctk_recent_manager_dispose (GObject *gobject)
407{
408 CtkRecentManager *manager = CTK_RECENT_MANAGER (gobject)((((CtkRecentManager*) (void *) g_type_check_instance_cast ((
GTypeInstance*) ((gobject)), ((ctk_recent_manager_get_type ()
))))))
;
409 CtkRecentManagerPrivate *priv = manager->priv;
410
411 if (priv->monitor != NULL((void*)0))
412 {
413 g_signal_handlers_disconnect_by_func (priv->monitor,g_signal_handlers_disconnect_matched ((priv->monitor), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (ctk_recent_manager_monitor_changed))), (manager
))
414 G_CALLBACK (ctk_recent_manager_monitor_changed),g_signal_handlers_disconnect_matched ((priv->monitor), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (ctk_recent_manager_monitor_changed))), (manager
))
415 manager)g_signal_handlers_disconnect_matched ((priv->monitor), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (ctk_recent_manager_monitor_changed))), (manager
))
;
416 g_object_unref (priv->monitor);
417 priv->monitor = NULL((void*)0);
418 }
419
420 if (priv->changed_timeout != 0)
421 {
422 g_source_remove (priv->changed_timeout);
423 priv->changed_timeout = 0;
424 priv->changed_age = 0;
425 }
426
427 if (priv->is_dirty)
428 {
429 g_object_ref (manager)((__typeof__ (manager)) (g_object_ref) (manager));
430 g_signal_emit (manager, signal_changed, 0);
431 g_object_unref (manager);
432 }
433
434 G_OBJECT_CLASS (ctk_recent_manager_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_recent_manager_parent_class)), (((GType) ((20) <<
(2))))))))
->dispose (gobject);
435}
436
437static void
438ctk_recent_manager_enabled_changed (CtkRecentManager *manager)
439{
440 manager->priv->is_dirty = TRUE(!(0));
441 ctk_recent_manager_changed (manager);
442}
443
444static void
445ctk_recent_manager_real_changed (CtkRecentManager *manager)
446{
447 CtkRecentManagerPrivate *priv = manager->priv;
448
449 g_object_freeze_notify (G_OBJECT (manager)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((manager)), (((GType) ((20) << (2))))))))
);
450
451 if (priv->is_dirty)
452 {
453 GError *write_error;
454
455 /* we are marked as dirty, so we dump the content of our
456 * recently used items list
457 */
458 if (!priv->recent_items)
459 {
460 /* if no container object has been defined, we create a new
461 * empty container, and dump it
462 */
463 priv->recent_items = g_bookmark_file_new ();
464 priv->size = 0;
465 }
466 else
467 {
468 CtkSettings *settings;
469 gint age;
470 gint max_size = MAX_LIST_SIZE1000;
471 gboolean enabled;
472
473 settings = ctk_settings_get_default ();
474 if (settings)
475 g_object_get (G_OBJECT (settings)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((settings)), (((GType) ((20) << (2))))))))
,
476 "ctk-recent-files-max-age", &age,
477 "ctk-recent-files-enabled", &enabled,
478 NULL((void*)0));
479 else
480 {
481 age = 30;
482 enabled = TRUE(!(0));
483 }
484
485 if (age == 0 || max_size == 0 || !enabled)
486 {
487 g_bookmark_file_free (priv->recent_items);
488 priv->recent_items = g_bookmark_file_new ();
489 priv->size = 0;
490 }
491 else
492 {
493 if (age > 0)
494 ctk_recent_manager_clamp_to_age (manager, age);
495 if (max_size > 0)
496 ctk_recent_manager_clamp_to_size (manager, max_size);
497 }
498 }
499
500 if (priv->filename != NULL((void*)0))
501 {
502 write_error = NULL((void*)0);
503 g_bookmark_file_to_file (priv->recent_items, priv->filename, &write_error);
504 if (write_error)
505 {
506 gchar *utf8 = g_filename_to_utf8 (priv->filename, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
507 g_warning ("Attempting to store changes into '%s', but failed: %s",
508 utf8 ? utf8 : "(invalid filename)",
509 write_error->message);
510 g_free (utf8);
511 g_error_free (write_error);
512 }
513
514 if (g_chmodchmod (priv->filename, 0600) < 0)
515 {
516 gchar *utf8 = g_filename_to_utf8 (priv->filename, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
517 g_warning ("Attempting to set the permissions of '%s', but failed: %s",
518 utf8 ? utf8 : "(invalid filename)",
519 g_strerror (errno(*__errno_location ())));
520 g_free (utf8);
521 }
522 }
523
524 /* mark us as clean */
525 priv->is_dirty = FALSE(0);
526 }
527 else
528 {
529 /* we are not marked as dirty, so we have been called
530 * because the recently used resources file has been
531 * changed (and not from us).
532 */
533 build_recent_items_list (manager);
534 }
535
536 g_object_thaw_notify (G_OBJECT (manager)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((manager)), (((GType) ((20) << (2))))))))
);
537}
538
539static void
540ctk_recent_manager_monitor_changed (GFileMonitor *monitor G_GNUC_UNUSED__attribute__ ((__unused__)),
541 GFile *file G_GNUC_UNUSED__attribute__ ((__unused__)),
542 GFile *other_file G_GNUC_UNUSED__attribute__ ((__unused__)),
543 GFileMonitorEvent event_type,
544 gpointer user_data)
545{
546 CtkRecentManager *manager = user_data;
547
548 switch (event_type)
549 {
550 case G_FILE_MONITOR_EVENT_CHANGED:
551 case G_FILE_MONITOR_EVENT_CREATED:
552 case G_FILE_MONITOR_EVENT_DELETED:
553 cdk_threads_enter ();
554 ctk_recent_manager_changed (manager);
555 cdk_threads_leave ();
556 break;
557
558 default:
559 break;
560 }
561}
562
563static gchar *
564get_default_filename (void)
565{
566 if (g_mkdir_with_parents (g_get_user_data_dir (), 0755) == -1)
567 {
568 int saved_errno = errno(*__errno_location ());
569
570 g_critical ("Unable to create user data directory '%s' for storing "
571 "the recently used files list: %s",
572 g_get_user_data_dir (),
573 g_strerror (saved_errno));
574
575 return NULL((void*)0);
576 }
577
578 return g_build_filename (g_get_user_data_dir (),
579 CTK_RECENTLY_USED_FILE"recently-used.xbel",
580 NULL((void*)0));
581}
582
583static void
584ctk_recent_manager_set_filename (CtkRecentManager *manager,
585 const gchar *filename)
586{
587 CtkRecentManagerPrivate *priv;
588 GFile *file;
589 GError *error;
590
591 g_assert (CTK_IS_RECENT_MANAGER (manager))do { if ((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; }))))) ; else g_assertion_message_expr ("Ctk", "ctkrecentmanager.c"
, 591, ((const char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"
); } while (0)
;
592
593 priv = manager->priv;
594
595 /* if a filename is already set and filename is not NULL, then copy
596 * it and reset the monitor; otherwise, if it's NULL we're being
597 * called from the finalization sequence, so we simply disconnect
598 * the monitoring and return.
599 *
600 * if no filename is set and filename is NULL, use the default.
601 */
602 if (priv->filename)
603 {
604 g_free (priv->filename);
605
606 if (priv->monitor)
607 {
608 g_signal_handlers_disconnect_by_func (priv->monitor,g_signal_handlers_disconnect_matched ((priv->monitor), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (ctk_recent_manager_monitor_changed))), (manager
))
609 G_CALLBACK (ctk_recent_manager_monitor_changed),g_signal_handlers_disconnect_matched ((priv->monitor), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (ctk_recent_manager_monitor_changed))), (manager
))
610 manager)g_signal_handlers_disconnect_matched ((priv->monitor), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (ctk_recent_manager_monitor_changed))), (manager
))
;
611 g_object_unref (priv->monitor);
612 priv->monitor = NULL((void*)0);
613 }
614
615 if (!filename || *filename == '\0')
616 return;
617 else
618 priv->filename = g_strdup (filename)g_strdup_inline (filename);
619 }
620 else
621 {
622 if (!filename || *filename == '\0')
623 priv->filename = get_default_filename ();
624 else
625 priv->filename = g_strdup (filename)g_strdup_inline (filename);
626 }
627
628 if (priv->filename != NULL((void*)0))
629 {
630 file = g_file_new_for_path (priv->filename);
631
632 error = NULL((void*)0);
633 priv->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL((void*)0), &error);
634 if (error)
635 {
636 gchar *utf8 = g_filename_to_utf8 (priv->filename, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
637 g_warning ("Unable to monitor '%s': %s\n"
638 "The CtkRecentManager will not update its contents "
639 "if the file is changed from other instances",
640 utf8 ? utf8 : "(invalid filename)",
641 error->message);
642 g_free (utf8);
643 g_error_free (error);
644 }
645 else
646 g_signal_connect (priv->monitor, "changed",g_signal_connect_data ((priv->monitor), ("changed"), (((GCallback
) (ctk_recent_manager_monitor_changed))), (manager), ((void*)
0), (GConnectFlags) 0)
647 G_CALLBACK (ctk_recent_manager_monitor_changed),g_signal_connect_data ((priv->monitor), ("changed"), (((GCallback
) (ctk_recent_manager_monitor_changed))), (manager), ((void*)
0), (GConnectFlags) 0)
648 manager)g_signal_connect_data ((priv->monitor), ("changed"), (((GCallback
) (ctk_recent_manager_monitor_changed))), (manager), ((void*)
0), (GConnectFlags) 0)
;
649
650 g_object_unref (file);
651 }
652
653 build_recent_items_list (manager);
654}
655
656/* reads the recently used resources file and builds the items list.
657 * we keep the items list inside the parser object, and build the
658 * RecentInfo object only on user’s demand to avoid useless replication.
659 * this function resets the dirty bit of the manager.
660 */
661static void
662build_recent_items_list (CtkRecentManager *manager)
663{
664 CtkRecentManagerPrivate *priv = manager->priv;
665 GError *read_error;
666 gint size;
667
668 if (!priv->recent_items)
669 {
670 priv->recent_items = g_bookmark_file_new ();
671 priv->size = 0;
672 }
673
674 if (priv->filename != NULL((void*)0))
675 {
676 /* the file exists, and it's valid (we hope); if not, destroy the container
677 * object and hope for a better result when the next "changed" signal is
678 * fired.
679 */
680 read_error = NULL((void*)0);
681 g_bookmark_file_load_from_file (priv->recent_items, priv->filename, &read_error);
682 if (read_error)
683 {
684 /* if the file does not exist we just wait for the first write
685 * operation on this recent manager instance, to avoid creating
686 * empty files and leading to spurious file system events (Sabayon
687 * will not be happy about those)
688 */
689 if (read_error->domain == G_FILE_ERRORg_file_error_quark () &&
690 read_error->code != G_FILE_ERROR_NOENT)
691 {
692 gchar *utf8 = g_filename_to_utf8 (priv->filename, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
693 g_warning ("Attempting to read the recently used resources "
694 "file at '%s', but the parser failed: %s.",
695 utf8 ? utf8 : "(invalid filename)",
696 read_error->message);
697 g_free (utf8);
698 }
699
700 g_bookmark_file_free (priv->recent_items);
701 priv->recent_items = NULL((void*)0);
702
703 g_error_free (read_error);
704 }
705 else
706 {
707 size = g_bookmark_file_get_size (priv->recent_items);
708 if (priv->size != size)
709 {
710 priv->size = size;
711
712 g_object_notify (G_OBJECT (manager)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((manager)), (((GType) ((20) << (2))))))))
, "size");
713 }
714 }
715 }
716
717 priv->is_dirty = FALSE(0);
718}
719
720
721/********************
722 * CtkRecentManager *
723 ********************/
724
725
726/**
727 * ctk_recent_manager_new:
728 *
729 * Creates a new recent manager object. Recent manager objects are used to
730 * handle the list of recently used resources. A #CtkRecentManager object
731 * monitors the recently used resources list, and emits the “changed” signal
732 * each time something inside the list changes.
733 *
734 * #CtkRecentManager objects are expensive: be sure to create them only when
735 * needed. You should use ctk_recent_manager_get_default() instead.
736 *
737 * Returns: A newly created #CtkRecentManager object
738 *
739 * Since: 2.10
740 */
741CtkRecentManager *
742ctk_recent_manager_new (void)
743{
744 return g_object_new (CTK_TYPE_RECENT_MANAGER(ctk_recent_manager_get_type ()), NULL((void*)0));
745}
746
747/**
748 * ctk_recent_manager_get_default:
749 *
750 * Gets a unique instance of #CtkRecentManager, that you can share
751 * in your application without caring about memory management.
752 *
753 * Returns: (transfer none): A unique #CtkRecentManager. Do not ref or
754 * unref it.
755 *
756 * Since: 2.10
757 */
758CtkRecentManager *
759ctk_recent_manager_get_default (void)
760{
761 if (G_UNLIKELY (!recent_manager_singleton)(!recent_manager_singleton))
762 recent_manager_singleton = ctk_recent_manager_new ();
763
764 return recent_manager_singleton;
765}
766
767
768static void
769ctk_recent_manager_add_item_query_info (GObject *source_object,
770 GAsyncResult *res,
771 gpointer user_data)
772{
773 GFile *file = G_FILE (source_object)((((GFile*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((source_object)), ((g_file_get_type ()))))))
;
774 CtkRecentManager *manager = user_data;
775 CtkRecentData recent_data;
776 GFileInfo *file_info;
777 gchar *uri, *basename, *content_type;
778
779 uri = g_file_get_uri (file);
780
781 file_info = g_file_query_info_finish (file, res, NULL((void*)0)); /* NULL-GError */
782
783 recent_data.display_name = NULL((void*)0);
784 recent_data.description = NULL((void*)0);
785
786 if (file_info)
787 {
788 content_type = g_file_info_get_attribute_as_string (file_info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE"standard::fast-content-type");
789
790 if (G_LIKELY (content_type)(content_type))
791 recent_data.mime_type = g_content_type_get_mime_type (content_type);
792 else
793 recent_data.mime_type = g_strdup (CTK_RECENT_DEFAULT_MIME)g_strdup_inline ("application/octet-stream");
794
795 g_free (content_type);
796 g_object_unref (file_info);
797 }
798 else
799 {
800 basename = g_file_get_basename (file);
801 content_type = g_content_type_guess (basename, NULL((void*)0), 0, NULL((void*)0));
802 recent_data.mime_type = g_content_type_get_mime_type (content_type);
803 g_free (basename);
804 g_free (content_type);
805 }
806
807 recent_data.app_name = g_strdup (g_get_application_name ())g_strdup_inline (g_get_application_name ());
808 recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL((void*)0));
809 recent_data.groups = NULL((void*)0);
810 recent_data.is_private = FALSE(0);
811
812 cdk_threads_enter ();
813
814 /* Ignore return value, this can't fail anyway since all required
815 * fields are set
816 */
817 ctk_recent_manager_add_full (manager, uri, &recent_data);
818
819 manager->priv->is_dirty = TRUE(!(0));
820 ctk_recent_manager_changed (manager);
821
822 cdk_threads_leave ();
823
824 g_free (recent_data.mime_type);
825 g_free (recent_data.app_name);
826 g_free (recent_data.app_exec);
827
828 g_object_unref (manager);
829 g_free (uri);
830}
831
832/**
833 * ctk_recent_manager_add_item:
834 * @manager: a #CtkRecentManager
835 * @uri: a valid URI
836 *
837 * Adds a new resource, pointed by @uri, into the recently used
838 * resources list.
839 *
840 * This function automatically retrieves some of the needed
841 * metadata and setting other metadata to common default values;
842 * it then feeds the data to ctk_recent_manager_add_full().
843 *
844 * See ctk_recent_manager_add_full() if you want to explicitly
845 * define the metadata for the resource pointed by @uri.
846 *
847 * Returns: %TRUE if the new item was successfully added
848 * to the recently used resources list
849 *
850 * Since: 2.10
851 */
852gboolean
853ctk_recent_manager_add_item (CtkRecentManager *manager,
854 const gchar *uri)
855{
856 GFile* file;
857
858 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (manager), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"); return
((0)); } } while (0)
;
859 g_return_val_if_fail (uri != NULL, FALSE)do { if ((uri != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "uri != NULL"); return (
(0)); } } while (0)
;
860
861 file = g_file_new_for_uri (uri);
862
863 g_file_query_info_async (file,
864 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE"standard::fast-content-type",
865 G_PRIORITY_DEFAULT0,
866 G_FILE_QUERY_INFO_NONE,
867 NULL((void*)0),
868 ctk_recent_manager_add_item_query_info,
869 g_object_ref (manager)((__typeof__ (manager)) (g_object_ref) (manager)));
870
871 g_object_unref (file);
872
873 return TRUE(!(0));
874}
875
876/**
877 * ctk_recent_manager_add_full:
878 * @manager: a #CtkRecentManager
879 * @uri: a valid URI
880 * @recent_data: metadata of the resource
881 *
882 * Adds a new resource, pointed by @uri, into the recently used
883 * resources list, using the metadata specified inside the
884 * #CtkRecentData-struct passed in @recent_data.
885 *
886 * The passed URI will be used to identify this resource inside the
887 * list.
888 *
889 * In order to register the new recently used resource, metadata about
890 * the resource must be passed as well as the URI; the metadata is
891 * stored in a #CtkRecentData-struct, which must contain the MIME
892 * type of the resource pointed by the URI; the name of the application
893 * that is registering the item, and a command line to be used when
894 * launching the item.
895 *
896 * Optionally, a #CtkRecentData-struct might contain a UTF-8 string
897 * to be used when viewing the item instead of the last component of
898 * the URI; a short description of the item; whether the item should
899 * be considered private - that is, should be displayed only by the
900 * applications that have registered it.
901 *
902 * Returns: %TRUE if the new item was successfully added to the
903 * recently used resources list, %FALSE otherwise
904 *
905 * Since: 2.10
906 */
907gboolean
908ctk_recent_manager_add_full (CtkRecentManager *manager,
909 const gchar *uri,
910 const CtkRecentData *data)
911{
912 CtkRecentManagerPrivate *priv;
913 CtkSettings *settings;
914 gboolean enabled;
915
916 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (manager), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"); return
((0)); } } while (0)
;
917 g_return_val_if_fail (uri != NULL, FALSE)do { if ((uri != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "uri != NULL"); return (
(0)); } } while (0)
;
918 g_return_val_if_fail (data != NULL, FALSE)do { if ((data != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "data != NULL"); return (
(0)); } } while (0)
;
919
920 /* sanity checks */
921 if ((data->display_name) &&
922 (!g_utf8_validate (data->display_name, -1, NULL((void*)0))))
923 {
924 g_warning ("Attempting to add '%s' to the list of recently used "
925 "resources, but the display name is not a valid UTF-8 "
926 "encoded string",
927 uri);
928 return FALSE(0);
929 }
930
931 if ((data->description) &&
932 (!g_utf8_validate (data->description, -1, NULL((void*)0))))
933 {
934 g_warning ("Attempting to add '%s' to the list of recently used "
935 "resources, but the description is not a valid UTF-8 "
936 "encoded string",
937 uri);
938 return FALSE(0);
939 }
940
941
942 if (!data->mime_type)
943 {
944 g_warning ("Attempting to add '%s' to the list of recently used "
945 "resources, but no MIME type was defined",
946 uri);
947 return FALSE(0);
948 }
949
950 if (!data->app_name)
951 {
952 g_warning ("Attempting to add '%s' to the list of recently used "
953 "resources, but no name of the application that is "
954 "registering it was defined",
955 uri);
956 return FALSE(0);
957 }
958
959 if (!data->app_exec)
960 {
961 g_warning ("Attempting to add '%s' to the list of recently used "
962 "resources, but no command line for the application "
963 "that is registering it was defined",
964 uri);
965 return FALSE(0);
966 }
967
968 settings = ctk_settings_get_default ();
969 g_object_get (G_OBJECT (settings)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((settings)), (((GType) ((20) << (2))))))))
, "ctk-recent-files-enabled", &enabled, NULL((void*)0));
970 if (!enabled)
971 return TRUE(!(0));
972
973 priv = manager->priv;
974
975 if (!priv->recent_items)
976 {
977 priv->recent_items = g_bookmark_file_new ();
978 priv->size = 0;
979 }
980
981 if (data->display_name)
982 g_bookmark_file_set_title (priv->recent_items, uri, data->display_name);
983
984 if (data->description)
985 g_bookmark_file_set_description (priv->recent_items, uri, data->description);
986
987 g_bookmark_file_set_mime_type (priv->recent_items, uri, data->mime_type);
988
989 if (data->groups && ((char*)data->groups)[0] != '\0')
990 {
991 gint j;
992
993 for (j = 0; (data->groups)[j] != NULL((void*)0); j++)
994 g_bookmark_file_add_group (priv->recent_items, uri, (data->groups)[j]);
995 }
996
997 /* register the application; this will take care of updating the
998 * registration count and time in case the application has
999 * already registered the same document inside the list
1000 */
1001 g_bookmark_file_add_application (priv->recent_items, uri,
1002 data->app_name,
1003 data->app_exec);
1004
1005 g_bookmark_file_set_is_private (priv->recent_items, uri,
1006 data->is_private);
1007
1008 /* mark us as dirty, so that when emitting the "changed" signal we
1009 * will dump our changes
1010 */
1011 priv->is_dirty = TRUE(!(0));
1012 ctk_recent_manager_changed (manager);
1013
1014 return TRUE(!(0));
1015}
1016
1017/**
1018 * ctk_recent_manager_remove_item:
1019 * @manager: a #CtkRecentManager
1020 * @uri: the URI of the item you wish to remove
1021 * @error: (allow-none): return location for a #GError, or %NULL
1022 *
1023 * Removes a resource pointed by @uri from the recently used resources
1024 * list handled by a recent manager.
1025 *
1026 * Returns: %TRUE if the item pointed by @uri has been successfully
1027 * removed by the recently used resources list, and %FALSE otherwise
1028 *
1029 * Since: 2.10
1030 */
1031gboolean
1032ctk_recent_manager_remove_item (CtkRecentManager *manager,
1033 const gchar *uri,
1034 GError **error)
1035{
1036 CtkRecentManagerPrivate *priv;
1037 GError *remove_error = NULL((void*)0);
1038
1039 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (manager), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"); return
((0)); } } while (0)
;
1040 g_return_val_if_fail (uri != NULL, FALSE)do { if ((uri != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "uri != NULL"); return (
(0)); } } while (0)
;
1041 g_return_val_if_fail (error == NULL || *error == NULL, FALSE)do { if ((error == ((void*)0) || *error == ((void*)0))) { } else
{ g_return_if_fail_warning ("Ctk", ((const char*) (__func__)
), "error == NULL || *error == NULL"); return ((0)); } } while
(0)
;
1042
1043 priv = manager->priv;
1044
1045 if (!priv->recent_items)
1046 {
1047 priv->recent_items = g_bookmark_file_new ();
1048 priv->size = 0;
1049
1050 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
1051 CTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1052 _("Unable to find an item with URI '%s'")((char *) g_dgettext ("ctk30", "Unable to find an item with URI '%s'"
))
,
1053 uri);
1054
1055 return FALSE(0);
1056 }
1057
1058 g_bookmark_file_remove_item (priv->recent_items, uri, &remove_error);
1059 if (remove_error)
1060 {
1061 g_error_free (remove_error);
1062
1063 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
1064 CTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1065 _("Unable to find an item with URI '%s'")((char *) g_dgettext ("ctk30", "Unable to find an item with URI '%s'"
))
,
1066 uri);
1067
1068 return FALSE(0);
1069 }
1070
1071 priv->is_dirty = TRUE(!(0));
1072 ctk_recent_manager_changed (manager);
1073
1074 return TRUE(!(0));
1075}
1076
1077/**
1078 * ctk_recent_manager_has_item:
1079 * @manager: a #CtkRecentManager
1080 * @uri: a URI
1081 *
1082 * Checks whether there is a recently used resource registered
1083 * with @uri inside the recent manager.
1084 *
1085 * Returns: %TRUE if the resource was found, %FALSE otherwise
1086 *
1087 * Since: 2.10
1088 */
1089gboolean
1090ctk_recent_manager_has_item (CtkRecentManager *manager,
1091 const gchar *uri)
1092{
1093 CtkRecentManagerPrivate *priv;
1094
1095 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (manager), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"); return
((0)); } } while (0)
;
1096 g_return_val_if_fail (uri != NULL, FALSE)do { if ((uri != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "uri != NULL"); return (
(0)); } } while (0)
;
1097
1098 priv = manager->priv;
1099 g_return_val_if_fail (priv->recent_items != NULL, FALSE)do { if ((priv->recent_items != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "priv->recent_items != NULL"
); return ((0)); } } while (0)
;
1100
1101 return g_bookmark_file_has_item (priv->recent_items, uri);
1102}
1103
1104static void
1105build_recent_info (GBookmarkFile *bookmarks,
1106 CtkRecentInfo *info)
1107{
1108 gchar **apps, **groups;
1109 gsize apps_len, groups_len, i;
1110 int app_index;
1111
1112 g_assert (bookmarks != NULL)do { if (bookmarks != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctkrecentmanager.c", 1112, ((const char*) (__func__
)), "bookmarks != NULL"); } while (0)
;
1113 g_assert (info != NULL)do { if (info != ((void*)0)) ; else g_assertion_message_expr (
"Ctk", "ctkrecentmanager.c", 1113, ((const char*) (__func__))
, "info != NULL"); } while (0)
;
1114
1115 info->display_name = g_bookmark_file_get_title (bookmarks, info->uri, NULL((void*)0));
1116 info->description = g_bookmark_file_get_description (bookmarks, info->uri, NULL((void*)0));
1117 info->mime_type = g_bookmark_file_get_mime_type (bookmarks, info->uri, NULL((void*)0));
1118
1119 info->is_private = g_bookmark_file_get_is_private (bookmarks, info->uri, NULL((void*)0));
1120
1121 info->added = g_bookmark_file_get_added_date_time (bookmarks, info->uri, NULL((void*)0));
1122 info->modified = g_bookmark_file_get_modified_date_time (bookmarks, info->uri, NULL((void*)0));
1123 info->visited = g_bookmark_file_get_visited_date_time (bookmarks, info->uri, NULL((void*)0));
1124
1125 groups = g_bookmark_file_get_groups (bookmarks, info->uri, &groups_len, NULL((void*)0));
1126 info->groups = g_malloc (sizeof (char *) * groups_len);
1127 info->n_groups = groups_len;
1128 for (i = 0; i < groups_len; i++)
1129 info->groups[i] = g_strdup (groups[i])g_strdup_inline (groups[i]);
1130
1131 g_strfreev (groups);
1132
1133 app_index = 0;
1134 apps = g_bookmark_file_get_applications (bookmarks, info->uri, &apps_len, NULL((void*)0));
1135 info->applications = g_malloc (sizeof (RecentAppInfo ) * apps_len);
1136 info->n_applications = 0;
1137 for (i = 0; i < apps_len; i++)
1138 {
1139 gchar *app_name, *app_exec;
1140 guint count;
1141 GDateTime *stamp;
1142 RecentAppInfo *app_info;
1143 gboolean res;
1144
1145 app_name = apps[i];
1146
1147 res = g_bookmark_file_get_application_info (bookmarks, info->uri, app_name,
1148 &app_exec,
1149 &count,
1150 &stamp,
1151 NULL((void*)0));
1152 if (!res)
1153 continue;
1154
1155 app_info = &info->applications[app_index];
1156 app_info->name= g_strdup (app_name)g_strdup_inline (app_name);
1157 app_info->exec = app_exec;
1158 app_info->count = count;
1159 app_info->stamp = g_date_time_ref (stamp);
1160
1161 g_hash_table_replace (info->apps_lookup, app_info->name, app_info);
1162
1163 app_index ++;
1164 info->n_applications ++;
1165 }
1166
1167 g_strfreev (apps);
1168}
1169
1170/**
1171 * ctk_recent_manager_lookup_item:
1172 * @manager: a #CtkRecentManager
1173 * @uri: a URI
1174 * @error: (allow-none): a return location for a #GError, or %NULL
1175 *
1176 * Searches for a URI inside the recently used resources list, and
1177 * returns a #CtkRecentInfo-struct containing informations about the resource
1178 * like its MIME type, or its display name.
1179 *
1180 * Returns: (nullable): a #CtkRecentInfo-struct containing information
1181 * about the resource pointed by @uri, or %NULL if the URI was
1182 * not registered in the recently used resources list. Free with
1183 * ctk_recent_info_unref().
1184 *
1185 * Since: 2.10
1186 */
1187CtkRecentInfo *
1188ctk_recent_manager_lookup_item (CtkRecentManager *manager,
1189 const gchar *uri,
1190 GError **error)
1191{
1192 CtkRecentManagerPrivate *priv;
1193 CtkRecentInfo *info = NULL((void*)0);
1194
1195 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (manager), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"); return
(((void*)0)); } } while (0)
;
1196 g_return_val_if_fail (uri != NULL, NULL)do { if ((uri != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "uri != NULL"); return (
((void*)0)); } } while (0)
;
1197 g_return_val_if_fail (error == NULL || *error == NULL, NULL)do { if ((error == ((void*)0) || *error == ((void*)0))) { } else
{ g_return_if_fail_warning ("Ctk", ((const char*) (__func__)
), "error == NULL || *error == NULL"); return (((void*)0)); }
} while (0)
;
1198
1199 priv = manager->priv;
1200 if (!priv->recent_items)
1201 {
1202 priv->recent_items = g_bookmark_file_new ();
1203 priv->size = 0;
1204
1205 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
1206 CTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1207 _("Unable to find an item with URI '%s'")((char *) g_dgettext ("ctk30", "Unable to find an item with URI '%s'"
))
,
1208 uri);
1209
1210 return NULL((void*)0);
1211 }
1212
1213 if (!g_bookmark_file_has_item (priv->recent_items, uri))
1214 {
1215 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
1216 CTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1217 _("Unable to find an item with URI '%s'")((char *) g_dgettext ("ctk30", "Unable to find an item with URI '%s'"
))
,
1218 uri);
1219 return NULL((void*)0);
1220 }
1221
1222 info = ctk_recent_info_new (uri);
1223 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1224
1225 /* fill the RecentInfo structure with the data retrieved by our
1226 * parser object from the storage file
1227 */
1228 build_recent_info (priv->recent_items, info);
1229
1230 return info;
1231}
1232
1233/**
1234 * ctk_recent_manager_move_item:
1235 * @manager: a #CtkRecentManager
1236 * @uri: the URI of a recently used resource
1237 * @new_uri: (allow-none): the new URI of the recently used resource, or
1238 * %NULL to remove the item pointed by @uri in the list
1239 * @error: (allow-none): a return location for a #GError, or %NULL
1240 *
1241 * Changes the location of a recently used resource from @uri to @new_uri.
1242 *
1243 * Please note that this function will not affect the resource pointed
1244 * by the URIs, but only the URI used in the recently used resources list.
1245 *
1246 * Returns: %TRUE on success
1247 *
1248 * Since: 2.10
1249 */
1250gboolean
1251ctk_recent_manager_move_item (CtkRecentManager *recent_manager,
1252 const gchar *uri,
1253 const gchar *new_uri,
1254 GError **error)
1255{
1256 CtkRecentManagerPrivate *priv;
1257 GError *move_error;
1258
1259 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (recent_manager), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((recent_manager)); GType __t = ((ctk_recent_manager_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (recent_manager)"
); return ((0)); } } while (0)
;
1260 g_return_val_if_fail (uri != NULL, FALSE)do { if ((uri != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "uri != NULL"); return (
(0)); } } while (0)
;
1261 g_return_val_if_fail (error == NULL || *error == NULL, FALSE)do { if ((error == ((void*)0) || *error == ((void*)0))) { } else
{ g_return_if_fail_warning ("Ctk", ((const char*) (__func__)
), "error == NULL || *error == NULL"); return ((0)); } } while
(0)
;
1262
1263 priv = recent_manager->priv;
1264
1265 if (!priv->recent_items)
1266 {
1267 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
1268 CTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1269 _("Unable to find an item with URI '%s'")((char *) g_dgettext ("ctk30", "Unable to find an item with URI '%s'"
))
,
1270 uri);
1271 return FALSE(0);
1272 }
1273
1274 if (!g_bookmark_file_has_item (priv->recent_items, uri))
1275 {
1276 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
1277 CTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1278 _("Unable to find an item with URI '%s'")((char *) g_dgettext ("ctk30", "Unable to find an item with URI '%s'"
))
,
1279 uri);
1280 return FALSE(0);
1281 }
1282
1283 move_error = NULL((void*)0);
1284 if (!g_bookmark_file_move_item (priv->recent_items,
1285 uri,
1286 new_uri,
1287 &move_error))
1288 {
1289 g_error_free (move_error);
1290
1291 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
1292 CTK_RECENT_MANAGER_ERROR_UNKNOWN,
1293 _("Unable to move the item with URI '%s' to '%s'")((char *) g_dgettext ("ctk30", "Unable to move the item with URI '%s' to '%s'"
))
,
1294 uri, new_uri);
1295 return FALSE(0);
1296 }
1297
1298 priv->is_dirty = TRUE(!(0));
1299 ctk_recent_manager_changed (recent_manager);
1300
1301 return TRUE(!(0));
1302}
1303
1304/**
1305 * ctk_recent_manager_get_items:
1306 * @manager: a #CtkRecentManager
1307 *
1308 * Gets the list of recently used resources.
1309 *
1310 * Returns: (element-type CtkRecentInfo) (transfer full): a list of
1311 * newly allocated #CtkRecentInfo objects. Use
1312 * ctk_recent_info_unref() on each item inside the list, and then
1313 * free the list itself using g_list_free().
1314 *
1315 * Since: 2.10
1316 */
1317GList *
1318ctk_recent_manager_get_items (CtkRecentManager *manager)
1319{
1320 CtkRecentManagerPrivate *priv;
1321 GList *retval = NULL((void*)0);
1322 gchar **uris;
1323 gsize uris_len, i;
1324
1325 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (manager), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"); return
(((void*)0)); } } while (0)
;
1326
1327 priv = manager->priv;
1328 if (!priv->recent_items)
1329 return NULL((void*)0);
1330
1331 uris = g_bookmark_file_get_uris (priv->recent_items, &uris_len);
1332 for (i = 0; i < uris_len; i++)
1333 {
1334 CtkRecentInfo *info;
1335
1336 info = ctk_recent_info_new (uris[i]);
1337 build_recent_info (priv->recent_items, info);
1338
1339 retval = g_list_prepend (retval, info);
1340 }
1341
1342 g_strfreev (uris);
1343
1344 return retval;
1345}
1346
1347static void
1348purge_recent_items_list (CtkRecentManager *manager,
1349 GError **error G_GNUC_UNUSED__attribute__ ((__unused__)))
1350{
1351 CtkRecentManagerPrivate *priv = manager->priv;
1352
1353 if (priv->recent_items == NULL((void*)0))
1354 return;
1355
1356 g_bookmark_file_free (priv->recent_items);
1357 priv->recent_items = g_bookmark_file_new ();
1358 priv->size = 0;
1359
1360 /* emit the changed signal, to ensure that the purge is written */
1361 priv->is_dirty = TRUE(!(0));
1362 ctk_recent_manager_changed (manager);
1363}
1364
1365/**
1366 * ctk_recent_manager_purge_items:
1367 * @manager: a #CtkRecentManager
1368 * @error: (allow-none): a return location for a #GError, or %NULL
1369 *
1370 * Purges every item from the recently used resources list.
1371 *
1372 * Returns: the number of items that have been removed from the
1373 * recently used resources list
1374 *
1375 * Since: 2.10
1376 */
1377gint
1378ctk_recent_manager_purge_items (CtkRecentManager *manager,
1379 GError **error)
1380{
1381 CtkRecentManagerPrivate *priv;
1382 gint count, purged;
1383
1384 g_return_val_if_fail (CTK_IS_RECENT_MANAGER (manager), -1)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((manager)); GType __t = ((ctk_recent_manager_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_RECENT_MANAGER (manager)"); return
(-1); } } while (0)
;
1385
1386 priv = manager->priv;
1387 if (!priv->recent_items)
1388 return 0;
1389
1390 count = g_bookmark_file_get_size (priv->recent_items);
1391 if (!count)
1392 return 0;
1393
1394 purge_recent_items_list (manager, error);
1395
1396 purged = count - g_bookmark_file_get_size (priv->recent_items);
1397
1398 return purged;
1399}
1400
1401static gboolean
1402emit_manager_changed (gpointer data)
1403{
1404 CtkRecentManager *manager = data;
1405
1406 manager->priv->changed_age = 0;
1407 manager->priv->changed_timeout = 0;
1408
1409 g_signal_emit (manager, signal_changed, 0);
1410
1411 return FALSE(0);
1412}
1413
1414static void
1415ctk_recent_manager_changed (CtkRecentManager *manager)
1416{
1417 /* coalesce consecutive changes
1418 *
1419 * we schedule a write in 250 msecs immediately; if we get more than one
1420 * request per millisecond before the timeout has a chance to run, we
1421 * schedule an emission immediately.
1422 */
1423 if (manager->priv->changed_timeout == 0)
1424 {
1425 manager->priv->changed_timeout = cdk_threads_add_timeout (250, emit_manager_changed, manager);
1426 g_source_set_name_by_id (manager->priv->changed_timeout, "[ctk+] emit_manager_changed");
1427 }
1428 else
1429 {
1430 manager->priv->changed_age += 1;
1431
1432 if (manager->priv->changed_age > 250)
1433 {
1434 g_source_remove (manager->priv->changed_timeout);
1435 g_signal_emit (manager, signal_changed, 0);
1436
1437 manager->priv->changed_age = 0;
1438 manager->priv->changed_timeout = 0;
1439 }
1440 }
1441}
1442
1443static void
1444ctk_recent_manager_clamp_to_age (CtkRecentManager *manager,
1445 gint age)
1446{
1447 CtkRecentManagerPrivate *priv = manager->priv;
1448 gchar **uris;
1449 gsize n_uris, i;
1450 GDateTime *now;
1451
1452 if (G_UNLIKELY (!priv->recent_items)(!priv->recent_items))
1453 return;
1454
1455 now = g_date_time_new_now_utc ();
1456
1457 uris = g_bookmark_file_get_uris (priv->recent_items, &n_uris);
1458
1459 for (i = 0; i < n_uris; i++)
1460 {
1461 const gchar *uri = uris[i];
1462 GDateTime *modified;
1463 gint item_age;
1464
1465 modified = g_bookmark_file_get_modified_date_time (priv->recent_items, uri, NULL((void*)0));
1466
1467 item_age = (int) (g_date_time_difference (now, modified) / (double)G_TIME_SPAN_DAY((86400000000L)));
1468 if (item_age > age)
1469 g_bookmark_file_remove_item (priv->recent_items, uri, NULL((void*)0));
1470 }
1471
1472 g_strfreev (uris);
1473}
1474
1475static void
1476ctk_recent_manager_clamp_to_size (CtkRecentManager *manager,
1477 const gint size)
1478{
1479 CtkRecentManagerPrivate *priv = manager->priv;
1480 gchar **uris;
1481 gsize n_uris, i;
1482
1483 if (G_UNLIKELY (!priv->recent_items)(!priv->recent_items) || G_UNLIKELY (size < 0)(size < 0))
1484 return;
1485
1486 uris = g_bookmark_file_get_uris (priv->recent_items, &n_uris);
1487
1488 if (n_uris < size)
1489 {
1490 g_strfreev (uris);
1491 return;
1492 }
1493
1494 for (i = 0; i < n_uris - size; i++)
1495 {
1496 const gchar *uri = uris[i];
1497 g_bookmark_file_remove_item (priv->recent_items, uri, NULL((void*)0));
1498 }
1499
1500 g_strfreev (uris);
1501}
1502
1503/*****************
1504 * CtkRecentInfo *
1505 *****************/
1506
1507G_DEFINE_BOXED_TYPE (CtkRecentInfo, ctk_recent_info,static GType ctk_recent_info_get_type_once (void); GType ctk_recent_info_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_recent_info_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_recent_info_get_type_once (void
) { GType (* _g_register_boxed) (const gchar *, union { CtkRecentInfo
* (*do_copy_type) (CtkRecentInfo *); CtkRecentInfo * (*do_const_copy_type
) (const CtkRecentInfo *); GBoxedCopyFunc do_copy_boxed; } __attribute__
((__transparent_union__)), union { void (* do_free_type) (CtkRecentInfo
*); GBoxedFreeFunc do_free_boxed; } __attribute__((__transparent_union__
)) ) = g_boxed_type_register_static; GType g_define_type_id =
_g_register_boxed (g_intern_static_string ("CtkRecentInfo"),
ctk_recent_info_ref, ctk_recent_info_unref); { {{};} } return
g_define_type_id; }
1508 ctk_recent_info_ref,static GType ctk_recent_info_get_type_once (void); GType ctk_recent_info_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_recent_info_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_recent_info_get_type_once (void
) { GType (* _g_register_boxed) (const gchar *, union { CtkRecentInfo
* (*do_copy_type) (CtkRecentInfo *); CtkRecentInfo * (*do_const_copy_type
) (const CtkRecentInfo *); GBoxedCopyFunc do_copy_boxed; } __attribute__
((__transparent_union__)), union { void (* do_free_type) (CtkRecentInfo
*); GBoxedFreeFunc do_free_boxed; } __attribute__((__transparent_union__
)) ) = g_boxed_type_register_static; GType g_define_type_id =
_g_register_boxed (g_intern_static_string ("CtkRecentInfo"),
ctk_recent_info_ref, ctk_recent_info_unref); { {{};} } return
g_define_type_id; }
1509 ctk_recent_info_unref)static GType ctk_recent_info_get_type_once (void); GType ctk_recent_info_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_recent_info_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_recent_info_get_type_once (void
) { GType (* _g_register_boxed) (const gchar *, union { CtkRecentInfo
* (*do_copy_type) (CtkRecentInfo *); CtkRecentInfo * (*do_const_copy_type
) (const CtkRecentInfo *); GBoxedCopyFunc do_copy_boxed; } __attribute__
((__transparent_union__)), union { void (* do_free_type) (CtkRecentInfo
*); GBoxedFreeFunc do_free_boxed; } __attribute__((__transparent_union__
)) ) = g_boxed_type_register_static; GType g_define_type_id =
_g_register_boxed (g_intern_static_string ("CtkRecentInfo"),
ctk_recent_info_ref, ctk_recent_info_unref); { {{};} } return
g_define_type_id; }
1510
1511static CtkRecentInfo *
1512ctk_recent_info_new (const gchar *uri)
1513{
1514 CtkRecentInfo *info;
1515
1516 g_assert (uri != NULL)do { if (uri != ((void*)0)) ; else g_assertion_message_expr (
"Ctk", "ctkrecentmanager.c", 1516, ((const char*) (__func__))
, "uri != NULL"); } while (0)
;
1517
1518 info = g_new0 (CtkRecentInfo, 1)((CtkRecentInfo *) g_malloc0_n ((1), sizeof (CtkRecentInfo)));
1519 info->uri = g_strdup (uri)g_strdup_inline (uri);
1520
1521 info->applications = NULL((void*)0);
1522 info->apps_lookup = g_hash_table_new (g_str_hash, g_str_equal);
1523
1524 info->groups = NULL((void*)0);
1525
1526 info->ref_count = 1;
1527
1528 return info;
1529}
1530
1531static void
1532ctk_recent_info_free (CtkRecentInfo *recent_info)
1533{
1534 int i;
1535
1536 if (!recent_info)
1537 return;
1538
1539 g_free (recent_info->uri);
1540 g_free (recent_info->display_name);
1541 g_free (recent_info->description);
1542 g_free (recent_info->mime_type);
1543
1544 for (i = 0; i < recent_info->n_applications; i ++)
1545 {
1546 const RecentAppInfo *app_info = &recent_info->applications[i];
1547
1548 g_free (app_info->name);
1549 g_free (app_info->exec);
1550 g_date_time_unref (app_info->stamp);
1551 }
1552 g_free (recent_info->applications);
1553
1554 if (recent_info->apps_lookup)
1555 g_hash_table_destroy (recent_info->apps_lookup);
1556
1557 for (i = 0; i < recent_info->n_groups; i ++)
1558 g_free (recent_info->groups[i]);
1559
1560 g_free (recent_info->groups);
1561
1562 if (recent_info->icon)
1563 g_object_unref (recent_info->icon);
1564
1565 g_free (recent_info);
1566}
1567
1568/**
1569 * ctk_recent_info_ref:
1570 * @info: a #CtkRecentInfo
1571 *
1572 * Increases the reference count of @recent_info by one.
1573 *
1574 * Returns: the recent info object with its reference count
1575 * increased by one
1576 *
1577 * Since: 2.10
1578 */
1579CtkRecentInfo *
1580ctk_recent_info_ref (CtkRecentInfo *info)
1581{
1582 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1583 g_return_val_if_fail (info->ref_count > 0, NULL)do { if ((info->ref_count > 0)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info->ref_count > 0"
); return (((void*)0)); } } while (0)
;
1584
1585 info->ref_count += 1;
1586
1587 return info;
1588}
1589
1590/**
1591 * ctk_recent_info_unref:
1592 * @info: a #CtkRecentInfo
1593 *
1594 * Decreases the reference count of @info by one. If the reference
1595 * count reaches zero, @info is deallocated, and the memory freed.
1596 *
1597 * Since: 2.10
1598 */
1599void
1600ctk_recent_info_unref (CtkRecentInfo *info)
1601{
1602 g_return_if_fail (info != NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return;
} } while (0)
;
1603 g_return_if_fail (info->ref_count > 0)do { if ((info->ref_count > 0)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info->ref_count > 0"
); return; } } while (0)
;
1604
1605 info->ref_count -= 1;
1606
1607 if (info->ref_count == 0)
1608 ctk_recent_info_free (info);
1609}
1610
1611/**
1612 * ctk_recent_info_get_uri:
1613 * @info: a #CtkRecentInfo
1614 *
1615 * Gets the URI of the resource.
1616 *
1617 * Returns: the URI of the resource. The returned string is
1618 * owned by the recent manager, and should not be freed.
1619 *
1620 * Since: 2.10
1621 */
1622const gchar *
1623ctk_recent_info_get_uri (CtkRecentInfo *info)
1624{
1625 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1626
1627 return info->uri;
1628}
1629
1630/**
1631 * ctk_recent_info_get_display_name:
1632 * @info: a #CtkRecentInfo
1633 *
1634 * Gets the name of the resource. If none has been defined, the basename
1635 * of the resource is obtained.
1636 *
1637 * Returns: the display name of the resource. The returned string
1638 * is owned by the recent manager, and should not be freed.
1639 *
1640 * Since: 2.10
1641 */
1642const gchar *
1643ctk_recent_info_get_display_name (CtkRecentInfo *info)
1644{
1645 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1646
1647 if (!info->display_name)
1648 info->display_name = ctk_recent_info_get_short_name (info);
1649
1650 return info->display_name;
1651}
1652
1653/**
1654 * ctk_recent_info_get_description:
1655 * @info: a #CtkRecentInfo
1656 *
1657 * Gets the (short) description of the resource.
1658 *
1659 * Returns: the description of the resource. The returned string
1660 * is owned by the recent manager, and should not be freed.
1661 *
1662 * Since: 2.10
1663 **/
1664const gchar *
1665ctk_recent_info_get_description (CtkRecentInfo *info)
1666{
1667 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1668
1669 return info->description;
1670}
1671
1672/**
1673 * ctk_recent_info_get_mime_type:
1674 * @info: a #CtkRecentInfo
1675 *
1676 * Gets the MIME type of the resource.
1677 *
1678 * Returns: the MIME type of the resource. The returned string
1679 * is owned by the recent manager, and should not be freed.
1680 *
1681 * Since: 2.10
1682 */
1683const gchar *
1684ctk_recent_info_get_mime_type (CtkRecentInfo *info)
1685{
1686 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1687
1688 if (!info->mime_type)
1689 info->mime_type = g_strdup (CTK_RECENT_DEFAULT_MIME)g_strdup_inline ("application/octet-stream");
1690
1691 return info->mime_type;
1692}
1693
1694/**
1695 * ctk_recent_info_get_added:
1696 * @info: a #CtkRecentInfo
1697 *
1698 * Gets the the time when the resource
1699 * was added to the recently used resources list.
1700 *
1701 * Returns: (transfer none): a #GDateTime for the time
1702 * when the resource was added
1703 *
1704 * Since: 2.10
1705 */
1706GDateTime *
1707ctk_recent_info_get_added (CtkRecentInfo *info)
1708{
1709 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1710
1711 return info->added;
1712}
1713
1714/**
1715 * ctk_recent_info_get_modified:
1716 * @info: a #CtkRecentInfo
1717 *
1718 * Gets the time when the meta-data
1719 * for the resource was last modified.
1720 *
1721 * Returns: (transfer none): a #GDateTime for the time
1722 * when the resource was last modified
1723 *
1724 * Since: 2.10
1725 */
1726GDateTime *
1727ctk_recent_info_get_modified (CtkRecentInfo *info)
1728{
1729 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1730
1731 return info->modified;
1732}
1733
1734/**
1735 * ctk_recent_info_get_visited:
1736 * @info: a #CtkRecentInfo
1737 *
1738 * Gets the time when the meta-data
1739 * for the resource was last visited.
1740 *
1741 * Returns: (transfer none): a #GDateTime for the time
1742 * when the resource was last visited
1743 *
1744 * Since: 2.10
1745 */
1746GDateTime *
1747ctk_recent_info_get_visited (CtkRecentInfo *info)
1748{
1749 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1750
1751 return info->visited;
1752}
1753
1754/**
1755 * ctk_recent_info_get_private_hint:
1756 * @info: a #CtkRecentInfo
1757 *
1758 * Gets the value of the “private” flag. Resources in the recently used
1759 * list that have this flag set to %TRUE should only be displayed by the
1760 * applications that have registered them.
1761 *
1762 * Returns: %TRUE if the private flag was found, %FALSE otherwise
1763 *
1764 * Since: 2.10
1765 */
1766gboolean
1767ctk_recent_info_get_private_hint (CtkRecentInfo *info)
1768{
1769 g_return_val_if_fail (info != NULL, FALSE)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
(0)); } } while (0)
;
1770
1771 return info->is_private;
1772}
1773
1774/**
1775 * ctk_recent_info_get_application_info:
1776 * @info: a #CtkRecentInfo
1777 * @app_name: the name of the application that has registered this item
1778 * @app_exec: (transfer none) (out): return location for the string containing
1779 * the command line
1780 * @count: (out): return location for the number of times this item was registered
1781 * @stamp: (out) (transfer none): return location for the time this item was last
1782 * registered for this application
1783 *
1784 * Gets the data regarding the application that has registered the resource
1785 * pointed by @info.
1786 *
1787 * If the command line contains any escape characters defined inside the
1788 * storage specification, they will be expanded.
1789 *
1790 * Returns: %TRUE if an application with @app_name has registered this
1791 * resource inside the recently used list, or %FALSE otherwise. The
1792 * @app_exec string is owned by the #CtkRecentInfo and should not be
1793 * modified or freed
1794 *
1795 * Since: 2.10
1796 */
1797gboolean
1798ctk_recent_info_get_application_info (CtkRecentInfo *info,
1799 const gchar *app_name,
1800 const gchar **app_exec,
1801 guint *count,
1802 GDateTime **stamp)
1803{
1804 RecentAppInfo *ai;
1805
1806 g_return_val_if_fail (info != NULL, FALSE)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
(0)); } } while (0)
;
1807 g_return_val_if_fail (app_name != NULL, FALSE)do { if ((app_name != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "app_name != NULL"); return
((0)); } } while (0)
;
1808
1809 ai = (RecentAppInfo *) g_hash_table_lookup (info->apps_lookup,
1810 app_name);
1811 if (!ai)
1812 {
1813 g_warning ("No registered application with name '%s' "
1814 "for item with URI '%s' found",
1815 app_name,
1816 info->uri);
1817 return FALSE(0);
1818 }
1819
1820 if (app_exec)
1821 *app_exec = ai->exec;
1822
1823 if (count)
1824 *count = ai->count;
1825
1826 if (stamp)
1827 *stamp = ai->stamp;
1828
1829 return TRUE(!(0));
1830}
1831
1832/**
1833 * ctk_recent_info_get_applications:
1834 * @info: a #CtkRecentInfo
1835 * @length: (out) (allow-none): return location for the length of the returned list
1836 *
1837 * Retrieves the list of applications that have registered this resource.
1838 *
1839 * Returns: (array length=length zero-terminated=1) (transfer full):
1840 * a newly allocated %NULL-terminated array of strings.
1841 * Use g_strfreev() to free it.
1842 *
1843 * Since: 2.10
1844 */
1845gchar **
1846ctk_recent_info_get_applications (CtkRecentInfo *info,
1847 gsize *length)
1848{
1849 gchar **retval;
1850 gsize n_apps, i;
1851
1852 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1853
1854 if (!info->applications)
1855 {
1856 if (length)
1857 *length = 0;
1858
1859 return NULL((void*)0);
1860 }
1861
1862 n_apps = info->n_applications;
1863
1864 retval = g_new0 (gchar *, n_apps + 1)((gchar * *) g_malloc0_n ((n_apps + 1), sizeof (gchar *)));
1865
1866 for (i = 0; i < info->n_applications; i ++)
1867 {
1868 const RecentAppInfo *ai = &info->applications[i];
1869
1870 retval[i] = g_strdup (ai->name)g_strdup_inline (ai->name);
1871 }
1872 retval[i] = NULL((void*)0);
1873
1874 if (length)
1875 *length = info->n_applications;
1876
1877 return retval;
1878}
1879
1880/**
1881 * ctk_recent_info_has_application:
1882 * @info: a #CtkRecentInfo
1883 * @app_name: a string containing an application name
1884 *
1885 * Checks whether an application registered this resource using @app_name.
1886 *
1887 * Returns: %TRUE if an application with name @app_name was found,
1888 * %FALSE otherwise
1889 *
1890 * Since: 2.10
1891 */
1892gboolean
1893ctk_recent_info_has_application (CtkRecentInfo *info,
1894 const gchar *app_name)
1895{
1896 g_return_val_if_fail (info != NULL, FALSE)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
(0)); } } while (0)
;
1897 g_return_val_if_fail (app_name != NULL, FALSE)do { if ((app_name != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "app_name != NULL"); return
((0)); } } while (0)
;
1898
1899 return (NULL((void*)0) != g_hash_table_lookup (info->apps_lookup, app_name));
1900}
1901
1902/**
1903 * ctk_recent_info_last_application:
1904 * @info: a #CtkRecentInfo
1905 *
1906 * Gets the name of the last application that have registered the
1907 * recently used resource represented by @info.
1908 *
1909 * Returns: an application name. Use g_free() to free it.
1910 *
1911 * Since: 2.10
1912 */
1913gchar *
1914ctk_recent_info_last_application (CtkRecentInfo *info)
1915{
1916 int i;
1917 GDateTime *last_stamp = NULL((void*)0);
1918 gchar *name = NULL((void*)0);
1919
1920 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
1921
1922 for (i = 0; i < info->n_applications; i ++)
1923 {
1924 const RecentAppInfo *ai = &info->applications[i];
1925
1926 if (last_stamp == NULL((void*)0) ||
1927 g_date_time_compare (ai->stamp, last_stamp) > 0)
1928 {
1929 name = ai->name;
1930 last_stamp = ai->stamp;
1931 }
1932 }
1933
1934 return g_strdup (name)g_strdup_inline (name);
1935}
1936
1937static GdkPixbuf *
1938get_icon_for_mime_type (const gchar *mime_type,
1939 gint pixel_size)
1940{
1941 CtkIconTheme *icon_theme;
1942 char *content_type;
1943 GIcon *icon;
1944 CtkIconInfo *info;
1945 GdkPixbuf *pixbuf;
1946
1947 icon_theme = ctk_icon_theme_get_default ();
1948
1949 content_type = g_content_type_from_mime_type (mime_type);
1950
1951 if (!content_type)
1952 return NULL((void*)0);
1953
1954 icon = g_content_type_get_icon (content_type);
1955 info = ctk_icon_theme_lookup_by_gicon (icon_theme,
1956 icon,
1957 pixel_size,
1958 CTK_ICON_LOOKUP_USE_BUILTIN);
1959 g_free (content_type);
1960 g_object_unref (icon);
1961
1962 if (!info)
1963 return NULL((void*)0);
1964
1965 pixbuf = ctk_icon_info_load_icon (info, NULL((void*)0));
1966 g_object_unref (info);
1967
1968 return pixbuf;
1969}
1970
1971static GdkPixbuf *
1972get_icon_fallback (const gchar *icon_name,
1973 gint size)
1974{
1975 CtkIconTheme *icon_theme;
1976 GdkPixbuf *retval;
1977
1978 icon_theme = ctk_icon_theme_get_default ();
1979
1980 retval = ctk_icon_theme_load_icon (icon_theme, icon_name,
1981 size,
1982 CTK_ICON_LOOKUP_USE_BUILTIN,
1983 NULL((void*)0));
1984 g_assert (retval != NULL)do { if (retval != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctkrecentmanager.c", 1984, ((const char*) (__func__
)), "retval != NULL"); } while (0)
;
1985
1986 return retval;
1987}
1988
1989/**
1990 * ctk_recent_info_get_icon:
1991 * @info: a #CtkRecentInfo
1992 * @size: the size of the icon in pixels
1993 *
1994 * Retrieves the icon of size @size associated to the resource MIME type.
1995 *
1996 * Returns: (nullable) (transfer full): a #GdkPixbuf containing the icon,
1997 * or %NULL. Use g_object_unref() when finished using the icon.
1998 *
1999 * Since: 2.10
2000 */
2001GdkPixbuf *
2002ctk_recent_info_get_icon (CtkRecentInfo *info,
2003 gint size)
2004{
2005 GdkPixbuf *retval = NULL((void*)0);
2006
2007 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
2008
2009 if (info->mime_type)
2010 retval = get_icon_for_mime_type (info->mime_type, size);
2011
2012 /* this function should never fail */
2013 if (!retval)
2014 {
2015 if (info->mime_type &&
2016 strcmp (info->mime_type, "x-directory/normal") == 0)
2017 retval = get_icon_fallback ("folder", size);
2018 else
2019 retval = get_icon_fallback ("text-x-generic", size);
2020 }
2021
2022 return retval;
2023}
2024
2025/**
2026 * ctk_recent_info_get_gicon:
2027 * @info: a #CtkRecentInfo
2028 *
2029 * Retrieves the icon associated to the resource MIME type.
2030 *
2031 * Returns: (nullable) (transfer full): a #GIcon containing the icon, or %NULL.
2032 * Use g_object_unref() when finished using the icon
2033 *
2034 * Since: 2.22
2035 */
2036GIcon *
2037ctk_recent_info_get_gicon (CtkRecentInfo *info)
2038{
2039 GIcon *icon = NULL((void*)0);
2040 gchar *content_type;
2041
2042 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
2043
2044 if (info->mime_type != NULL((void*)0) &&
2045 (content_type = g_content_type_from_mime_type (info->mime_type)) != NULL((void*)0))
2046 {
2047 icon = g_content_type_get_icon (content_type);
2048 g_free (content_type);
2049 }
2050
2051 return icon;
2052}
2053
2054/**
2055 * ctk_recent_info_is_local:
2056 * @info: a #CtkRecentInfo
2057 *
2058 * Checks whether the resource is local or not by looking at the
2059 * scheme of its URI.
2060 *
2061 * Returns: %TRUE if the resource is local
2062 *
2063 * Since: 2.10
2064 */
2065gboolean
2066ctk_recent_info_is_local (CtkRecentInfo *info)
2067{
2068 g_return_val_if_fail (info != NULL, FALSE)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
(0)); } } while (0)
;
2069
2070 return has_case_prefix (info->uri, "file:/");
2071}
2072
2073/**
2074 * ctk_recent_info_exists:
2075 * @info: a #CtkRecentInfo
2076 *
2077 * Checks whether the resource pointed by @info still exists.
2078 * At the moment this check is done only on resources pointing
2079 * to local files.
2080 *
2081 * Returns: %TRUE if the resource exists
2082 *
2083 * Since: 2.10
2084 */
2085gboolean
2086ctk_recent_info_exists (CtkRecentInfo *info)
2087{
2088 gchar *filename;
2089 GStatBuf stat_buf;
2090 gboolean retval = FALSE(0);
2091
2092 g_return_val_if_fail (info != NULL, FALSE)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
(0)); } } while (0)
;
2093
2094 /* we guarantee only local resources */
2095 if (!ctk_recent_info_is_local (info))
2096 return FALSE(0);
2097
2098 filename = g_filename_from_uri (info->uri, NULL((void*)0), NULL((void*)0));
2099 if (filename)
2100 {
2101 if (g_statstat (filename, &stat_buf) == 0)
2102 retval = TRUE(!(0));
2103
2104 g_free (filename);
2105 }
2106
2107 return retval;
2108}
2109
2110/**
2111 * ctk_recent_info_match:
2112 * @info_a: a #CtkRecentInfo
2113 * @info_b: a #CtkRecentInfo
2114 *
2115 * Checks whether two #CtkRecentInfo-struct point to the same
2116 * resource.
2117 *
2118 * Returns: %TRUE if both #CtkRecentInfo-struct point to the same
2119 * resource, %FALSE otherwise
2120 *
2121 * Since: 2.10
2122 */
2123gboolean
2124ctk_recent_info_match (CtkRecentInfo *info_a,
2125 CtkRecentInfo *info_b)
2126{
2127 g_return_val_if_fail (info_a != NULL, FALSE)do { if ((info_a != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info_a != NULL"); return
((0)); } } while (0)
;
2128 g_return_val_if_fail (info_b != NULL, FALSE)do { if ((info_b != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info_b != NULL"); return
((0)); } } while (0)
;
2129
2130 return (0 == strcmp (info_a->uri, info_b->uri));
2131}
2132
2133/* taken from gnome-vfs-uri.c */
2134static const gchar *
2135get_method_string (const gchar *substring,
2136 gchar **method_string)
2137{
2138 const gchar *p;
2139 char *method;
2140
2141 for (p = substring;
2142 g_ascii_isalnum (*p)((g_ascii_table[(guchar) (*p)] & G_ASCII_ALNUM) != 0) || *p == '+' || *p == '-' || *p == '.';
2143 p++)
2144 ;
2145
2146 if (*p == ':'
2147#ifdef G_OS_WIN32
2148 &&
2149 !(p == substring + 1 && g_ascii_isalpha (*substring)((g_ascii_table[(guchar) (*substring)] & G_ASCII_ALPHA) !=
0)
)
2150#endif
2151 )
2152 {
2153 /* Found toplevel method specification. */
2154 method = g_strndup (substring, p - substring);
2155 *method_string = g_ascii_strdown (method, -1);
2156 g_free (method);
2157 p++;
2158 }
2159 else
2160 {
2161 *method_string = g_strdup ("file")g_strdup_inline ("file");
2162 p = substring;
2163 }
2164
2165 return p;
2166}
2167
2168/* Stolen from gnome_vfs_make_valid_utf8() */
2169static char *
2170make_valid_utf8 (const char *name)
2171{
2172 GString *string;
2173 const char *remainder, *invalid;
2174 int remaining_bytes, valid_bytes;
2175
2176 string = NULL((void*)0);
2177 remainder = name;
2178 remaining_bytes = name ? strlen (name) : 0;
2179
2180 while (remaining_bytes != 0)
2181 {
2182 if (g_utf8_validate (remainder, remaining_bytes, &invalid))
2183 break;
2184
2185 valid_bytes = invalid - remainder;
2186
2187 if (string == NULL((void*)0))
2188 string = g_string_sized_new (remaining_bytes);
2189
2190 g_string_append_len (string, remainder, valid_bytes)g_string_append_len_inline (string, remainder, valid_bytes);
2191 g_string_append_c (string, '?')g_string_append_c_inline (string, '?');
2192
2193 remaining_bytes -= valid_bytes + 1;
2194 remainder = invalid + 1;
2195 }
2196
2197 if (string == NULL((void*)0))
2198 return g_strdup (name)g_strdup_inline (name);
2199
2200 g_string_append (string, remainder)(__builtin_constant_p (remainder) ? __extension__ ({ const char
* const __val = (remainder); g_string_append_len_inline (string
, __val, (__val != ((void*)0)) ? (gssize) strlen (((__val) + !
(__val))) : (gssize) -1); }) : g_string_append_len_inline (string
, remainder, (gssize) -1))
;
2201 g_assert (g_utf8_validate (string->str, -1, NULL))do { if (g_utf8_validate (string->str, -1, ((void*)0))) ; else
g_assertion_message_expr ("Ctk", "ctkrecentmanager.c", 2201,
((const char*) (__func__)), "g_utf8_validate (string->str, -1, NULL)"
); } while (0)
;
2202
2203 return g_string_free (string, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((string
), ((0))) : g_string_free_and_steal (string)) : (g_string_free
) ((string), ((0))))
;
2204}
2205
2206static gchar *
2207get_uri_shortname_for_display (const gchar *uri)
2208{
2209 gchar *name = NULL((void*)0);
2210 gboolean validated = FALSE(0);
2211
2212 if (has_case_prefix (uri, "file:/"))
2213 {
2214 gchar *local_file;
2215
2216 local_file = g_filename_from_uri (uri, NULL((void*)0), NULL((void*)0));
2217
2218 if (local_file)
2219 {
2220 name = g_filename_display_basename (local_file);
2221 validated = TRUE(!(0));
2222 }
2223
2224 g_free (local_file);
2225 }
2226
2227 if (!name)
2228 {
2229 gchar *method;
2230 gchar *local_file;
2231 const gchar *rest;
2232
2233 rest = get_method_string (uri, &method);
2234 local_file = g_filename_display_basename (rest);
2235
2236 name = g_strconcat (method, ": ", local_file, NULL((void*)0));
2237
2238 g_free (local_file);
2239 g_free (method);
2240 }
2241
2242 g_assert (name != NULL)do { if (name != ((void*)0)) ; else g_assertion_message_expr (
"Ctk", "ctkrecentmanager.c", 2242, ((const char*) (__func__))
, "name != NULL"); } while (0)
;
2243
2244 if (!validated && !g_utf8_validate (name, -1, NULL((void*)0)))
2245 {
2246 gchar *utf8_name;
2247
2248 utf8_name = make_valid_utf8 (name);
2249 g_free (name);
2250
2251 name = utf8_name;
2252 }
2253
2254 return name;
2255}
2256
2257/**
2258 * ctk_recent_info_get_short_name:
2259 * @info: an #CtkRecentInfo
2260 *
2261 * Computes a valid UTF-8 string that can be used as the
2262 * name of the item in a menu or list. For example, calling
2263 * this function on an item that refers to
2264 * “file:///foo/bar.txt” will yield “bar.txt”.
2265 *
2266 * Returns: A newly-allocated string in UTF-8 encoding
2267 * free it with g_free()
2268 *
2269 * Since: 2.10
2270 */
2271gchar *
2272ctk_recent_info_get_short_name (CtkRecentInfo *info)
2273{
2274 gchar *short_name;
2275
2276 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
2277
2278 if (info->uri == NULL((void*)0))
2279 return NULL((void*)0);
2280
2281 short_name = get_uri_shortname_for_display (info->uri);
2282
2283 return short_name;
2284}
2285
2286/**
2287 * ctk_recent_info_get_uri_display:
2288 * @info: a #CtkRecentInfo
2289 *
2290 * Gets a displayable version of the resource’s URI. If the resource
2291 * is local, it returns a local path; if the resource is not local,
2292 * it returns the UTF-8 encoded content of ctk_recent_info_get_uri().
2293 *
2294 * Returns: (nullable): a newly allocated UTF-8 string containing the
2295 * resource’s URI or %NULL. Use g_free() when done using it.
2296 *
2297 * Since: 2.10
2298 */
2299gchar *
2300ctk_recent_info_get_uri_display (CtkRecentInfo *info)
2301{
2302 gchar *retval;
2303
2304 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
2305
2306 retval = NULL((void*)0);
2307 if (ctk_recent_info_is_local (info))
2308 {
2309 gchar *filename;
2310
2311 filename = g_filename_from_uri (info->uri, NULL((void*)0), NULL((void*)0));
This statement is never executed
2312 if (!filename)
2313 return NULL((void*)0);
2314
2315 retval = g_filename_to_utf8 (filename, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
2316 g_free (filename);
2317 }
2318 else
2319 {
2320 retval = make_valid_utf8 (info->uri);
2321 }
2322
2323 return retval;
2324}
2325
2326/**
2327 * ctk_recent_info_get_age:
2328 * @info: a #CtkRecentInfo
2329 *
2330 * Gets the number of days elapsed since the last update
2331 * of the resource pointed by @info.
2332 *
2333 * Returns: a positive integer containing the number of days
2334 * elapsed since the time this resource was last modified
2335 *
2336 * Since: 2.10
2337 */
2338gint
2339ctk_recent_info_get_age (CtkRecentInfo *info)
2340{
2341 GDateTime *now;
2342
2343 g_return_val_if_fail (info != NULL, -1)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
-1); } } while (0)
;
2344
2345 now = g_date_time_new_now_utc ();
2346
2347 return (int) (g_date_time_difference (now, info->modified) / (double)G_TIME_SPAN_DAY((86400000000L)));
2348}
2349
2350/**
2351 * ctk_recent_info_get_groups:
2352 * @info: a #CtkRecentInfo
2353 * @length: (out) (allow-none): return location for the number of groups returned
2354 *
2355 * Returns all groups registered for the recently used item @info.
2356 * The array of returned group names will be %NULL terminated, so
2357 * length might optionally be %NULL.
2358 *
2359 * Returns: (array length=length zero-terminated=1) (transfer full):
2360 * a newly allocated %NULL terminated array of strings.
2361 * Use g_strfreev() to free it.
2362 *
2363 * Since: 2.10
2364 */
2365gchar **
2366ctk_recent_info_get_groups (CtkRecentInfo *info,
2367 gsize *length)
2368{
2369 gchar **retval;
2370 gsize n_groups, i;
2371
2372 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
2373
2374 if (!info->groups || info->n_groups == 0)
2375 {
2376 if (length)
2377 *length = 0;
2378
2379 return NULL((void*)0);
2380 }
2381
2382 n_groups = info->n_groups;
2383
2384 retval = g_new0 (gchar *, n_groups + 1)((gchar * *) g_malloc0_n ((n_groups + 1), sizeof (gchar *)));
2385
2386 for (i = 0; i < info->n_groups; i ++)
2387 retval[i] = g_strdup (info->groups[i])g_strdup_inline (info->groups[i]);
2388
2389 retval[i] = NULL((void*)0);
2390
2391 if (length)
2392 *length = info->n_groups;
2393
2394 return retval;
2395}
2396
2397/**
2398 * ctk_recent_info_has_group:
2399 * @info: a #CtkRecentInfo
2400 * @group_name: name of a group
2401 *
2402 * Checks whether @group_name appears inside the groups
2403 * registered for the recently used item @info.
2404 *
2405 * Returns: %TRUE if the group was found
2406 *
2407 * Since: 2.10
2408 */
2409gboolean
2410ctk_recent_info_has_group (CtkRecentInfo *info,
2411 const gchar *group_name)
2412{
2413 int i;
2414
2415 g_return_val_if_fail (info != NULL, FALSE)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
(0)); } } while (0)
;
2416 g_return_val_if_fail (group_name != NULL, FALSE)do { if ((group_name != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "group_name != NULL"); return
((0)); } } while (0)
;
2417
2418 if (!info->groups)
2419 return FALSE(0);
2420
2421 for (i = 0; i < info->n_groups; i ++)
2422 {
2423 const char *g = info->groups[i];
2424
2425 if (strcmp (g, group_name) == 0)
2426 return TRUE(!(0));
2427 }
2428
2429 return FALSE(0);
2430}
2431
2432/**
2433 * ctk_recent_info_create_app_info:
2434 * @info: a #CtkRecentInfo
2435 * @app_name: (allow-none): the name of the application that should
2436 * be mapped to a #GAppInfo; if %NULL is used then the default
2437 * application for the MIME type is used
2438 * @error: (allow-none): return location for a #GError, or %NULL
2439 *
2440 * Creates a #GAppInfo for the specified #CtkRecentInfo
2441 *
2442 * Returns: (nullable) (transfer full): the newly created #GAppInfo, or %NULL.
2443 * In case of error, @error will be set either with a
2444 * %CTK_RECENT_MANAGER_ERROR or a %G_IO_ERROR
2445 */
2446GAppInfo *
2447ctk_recent_info_create_app_info (CtkRecentInfo *info,
2448 const gchar *app_name,
2449 GError **error)
2450{
2451 RecentAppInfo *ai;
2452 GAppInfo *app_info;
2453 GError *internal_error = NULL((void*)0);
2454
2455 g_return_val_if_fail (info != NULL, NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "info != NULL"); return (
((void*)0)); } } while (0)
;
2456
2457 if (app_name == NULL((void*)0) || *app_name == '\0')
2458 {
2459 char *content_type;
2460
2461 if (info->mime_type == NULL((void*)0))
2462 return NULL((void*)0);
2463
2464 content_type = g_content_type_from_mime_type (info->mime_type);
2465 if (content_type == NULL((void*)0))
2466 return NULL((void*)0);
2467
2468 app_info = g_app_info_get_default_for_type (content_type, TRUE(!(0)));
2469 g_free (content_type);
2470
2471 return app_info;
2472 }
2473
2474 ai = g_hash_table_lookup (info->apps_lookup, app_name);
2475 if (ai == NULL((void*)0))
2476 {
2477 g_set_error (error, CTK_RECENT_MANAGER_ERROR(ctk_recent_manager_error_quark ()),
2478 CTK_RECENT_MANAGER_ERROR_NOT_REGISTERED,
2479 _("No registered application with name '%s' for item with URI '%s' found")((char *) g_dgettext ("ctk30", "No registered application with name '%s' for item with URI '%s' found"
))
,
2480 app_name,
2481 info->uri);
2482 return NULL((void*)0);
2483 }
2484
2485 internal_error = NULL((void*)0);
2486 app_info = g_app_info_create_from_commandline (ai->exec, ai->name,
2487 G_APP_INFO_CREATE_NONE,
2488 &internal_error);
2489 if (internal_error != NULL((void*)0))
2490 {
2491 g_propagate_error (error, internal_error);
2492 return NULL((void*)0);
2493 }
2494
2495 return app_info;
2496}
2497
2498/*
2499 * _ctk_recent_manager_sync:
2500 *
2501 * Private function for synchronising the recent manager singleton.
2502 */
2503void
2504_ctk_recent_manager_sync (void)
2505{
2506 if (recent_manager_singleton)
2507 {
2508 /* force a dump of the contents of the recent manager singleton */
2509 recent_manager_singleton->priv->is_dirty = TRUE(!(0));
2510 ctk_recent_manager_real_changed (recent_manager_singleton);
2511 }
2512}