Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-contacts"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <libpeas/peas.h>
10 : : #include <libvalent-core.h>
11 : :
12 : : #include "valent-contacts.h"
13 : : #include "valent-contacts-adapter.h"
14 : : #include "valent-contact-store.h"
15 : : #include "valent-contact-cache-private.h"
16 : :
17 : :
18 : : /**
19 : : * ValentContacts:
20 : : *
21 : : * A class for managing address books.
22 : : *
23 : : * #ValentContacts is an address book manager, intended for use by
24 : : * [class@Valent.DevicePlugin] implementations.
25 : : *
26 : : * Plugins can implement [class@Valent.ContactsAdapter] to provide an interface
27 : : * to manage instances of [class@Valent.ContactStore].
28 : : *
29 : : * Since: 1.0
30 : : */
31 : :
32 : : struct _ValentContacts
33 : : {
34 : : ValentComponent parent_instance;
35 : :
36 : : GPtrArray *adapters;
37 : : GPtrArray *stores;
38 : : };
39 : :
40 : : static void g_list_model_iface_init (GListModelInterface *iface);
41 : :
42 [ + + + - ]: 237 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentContacts, valent_contacts, VALENT_TYPE_COMPONENT,
43 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
44 : :
45 : : static ValentContacts *default_contacts = NULL;
46 : :
47 : :
48 : : static void
49 : 25 : on_items_changed (GListModel *list,
50 : : unsigned int position,
51 : : unsigned int removed,
52 : : unsigned int added,
53 : : ValentContacts *self)
54 : : {
55 : 25 : unsigned int real_position = 0;
56 : :
57 : 25 : VALENT_ENTRY;
58 : :
59 [ + - ]: 25 : g_assert (VALENT_IS_CONTACTS_ADAPTER (list));
60 [ + - ]: 25 : g_assert (VALENT_IS_CONTACTS (self));
61 : :
62 : : /* Translate the adapter position */
63 [ + - ]: 25 : for (unsigned int i = 0; i < self->adapters->len; i++)
64 : : {
65 : 25 : GListModel *adapter = g_ptr_array_index (self->adapters, i);
66 : :
67 [ - + ]: 25 : if (adapter == list)
68 : : break;
69 : :
70 : 0 : real_position += g_list_model_get_n_items (adapter);
71 : : }
72 : :
73 : 25 : real_position += position;
74 : :
75 : : /* Propagate the changes */
76 [ + + ]: 30 : for (unsigned int i = 0; i < removed; i++)
77 : 5 : g_ptr_array_remove_index (self->stores, real_position);
78 : :
79 [ + + ]: 36 : for (unsigned int i = 0; i < added; i++)
80 : 11 : g_ptr_array_insert (self->stores,
81 : 11 : real_position + i,
82 : : g_list_model_get_item (list, position + i));
83 : :
84 : 25 : g_list_model_items_changed (G_LIST_MODEL (self), real_position, removed, added);
85 : :
86 : 25 : VALENT_EXIT;
87 : : }
88 : :
89 : : /*
90 : : * GListModel
91 : : */
92 : : static gpointer
93 : 6 : valent_contacts_get_item (GListModel *list,
94 : : unsigned int position)
95 : : {
96 : 6 : ValentContacts *self = VALENT_CONTACTS (list);
97 : :
98 [ + - ]: 6 : g_assert (VALENT_IS_CONTACTS (self));
99 : :
100 [ + - ]: 6 : if G_UNLIKELY (position >= self->stores->len)
101 : : return NULL;
102 : :
103 : 6 : return g_object_ref (g_ptr_array_index (self->stores, position));
104 : : }
105 : :
106 : : static GType
107 : 0 : valent_contacts_get_item_type (GListModel *list)
108 : : {
109 : 0 : return VALENT_TYPE_CONTACT_STORE;
110 : : }
111 : :
112 : : static unsigned int
113 : 5 : valent_contacts_get_n_items (GListModel *list)
114 : : {
115 : 5 : ValentContacts *self = VALENT_CONTACTS (list);
116 : :
117 [ + - ]: 5 : g_assert (VALENT_IS_CONTACTS (self));
118 : :
119 : 5 : return self->stores->len;
120 : : }
121 : :
122 : : static void
123 : 8 : g_list_model_iface_init (GListModelInterface *iface)
124 : : {
125 : 8 : iface->get_item = valent_contacts_get_item;
126 : 8 : iface->get_item_type = valent_contacts_get_item_type;
127 : 8 : iface->get_n_items = valent_contacts_get_n_items;
128 : 8 : }
129 : :
130 : : /*
131 : : * ValentComponent
132 : : */
133 : : static void
134 : 9 : valent_contacts_bind_extension (ValentComponent *component,
135 : : GObject *extension)
136 : : {
137 : 9 : ValentContacts *self = VALENT_CONTACTS (component);
138 : 9 : GListModel *list = G_LIST_MODEL (extension);
139 : :
140 : 9 : VALENT_ENTRY;
141 : :
142 [ + - ]: 9 : g_assert (VALENT_IS_CONTACTS (self));
143 [ - + ]: 9 : g_assert (VALENT_IS_CONTACTS_ADAPTER (extension));
144 : :
145 : 9 : g_ptr_array_add (self->adapters, g_object_ref (extension));
146 : 9 : on_items_changed (list, 0, 0, g_list_model_get_n_items (list), self);
147 : 9 : g_signal_connect_object (list,
148 : : "items-changed",
149 : : G_CALLBACK (on_items_changed),
150 : : self,
151 : : 0);
152 : :
153 : 9 : VALENT_EXIT;
154 : : }
155 : :
156 : : static void
157 : 3 : valent_contacts_unbind_extension (ValentComponent *component,
158 : : GObject *extension)
159 : : {
160 : 3 : ValentContacts *self = VALENT_CONTACTS (component);
161 : 3 : GListModel *list = G_LIST_MODEL (extension);
162 : :
163 : 3 : VALENT_ENTRY;
164 : :
165 [ + - ]: 3 : g_assert (VALENT_IS_CONTACTS (self));
166 [ - + ]: 3 : g_assert (VALENT_IS_CONTACTS_ADAPTER (extension));
167 : :
168 [ - + ]: 3 : if (!g_ptr_array_find (self->adapters, extension, NULL))
169 : : {
170 : 0 : g_warning ("No such adapter \"%s\" found in \"%s\"",
171 : : G_OBJECT_TYPE_NAME (extension),
172 : : G_OBJECT_TYPE_NAME (component));
173 : 0 : return;
174 : : }
175 : :
176 : 3 : g_signal_handlers_disconnect_by_func (list, on_items_changed, self);
177 : 3 : on_items_changed (list, 0, g_list_model_get_n_items (list), 0, self);
178 : 3 : g_ptr_array_remove (self->adapters, extension);
179 : :
180 : 3 : VALENT_EXIT;
181 : : }
182 : :
183 : : /*
184 : : * ValentContacts
185 : : */
186 : : static ValentContactStore *
187 : 5 : valent_contacts_create_store (const char *uid,
188 : : const char *name,
189 : : const char *icon_name)
190 : : {
191 : 10 : g_autoptr (ESource) source = NULL;
192 : 5 : ESourceBackend *backend;
193 : :
194 [ + - + - ]: 5 : g_assert (uid != NULL && *uid != '\0');
195 [ + - - + ]: 5 : g_assert (name != NULL && *name != '\0');
196 : :
197 : : /* Create a scratch source for a local addressbook source */
198 : 5 : source = e_source_new_with_uid (uid, NULL, NULL);
199 : 5 : backend = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
200 : 5 : e_source_backend_set_backend_name (backend, "local");
201 : 5 : e_source_set_display_name (source, name);
202 : :
203 [ + - ]: 5 : return g_object_new (VALENT_TYPE_CONTACT_CACHE,
204 : : "source", source,
205 : : NULL);
206 : : }
207 : :
208 : : /*
209 : : * GObject
210 : : */
211 : : static void
212 : 3 : valent_contacts_finalize (GObject *object)
213 : : {
214 : 3 : ValentContacts *self = VALENT_CONTACTS (object);
215 : :
216 [ + - ]: 3 : g_clear_pointer (&self->adapters, g_ptr_array_unref);
217 [ + - ]: 3 : g_clear_pointer (&self->stores, g_ptr_array_unref);
218 : :
219 : 3 : G_OBJECT_CLASS (valent_contacts_parent_class)->finalize (object);
220 : 3 : }
221 : :
222 : : static void
223 : 8 : valent_contacts_class_init (ValentContactsClass *klass)
224 : : {
225 : 8 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
226 : 8 : ValentComponentClass *component_class = VALENT_COMPONENT_CLASS (klass);
227 : :
228 : : /* Ensure we don't hit a MT race condition */
229 : 8 : g_type_ensure (E_TYPE_SOURCE);
230 : :
231 : 8 : object_class->finalize = valent_contacts_finalize;
232 : :
233 : 8 : component_class->bind_extension = valent_contacts_bind_extension;
234 : 8 : component_class->unbind_extension = valent_contacts_unbind_extension;
235 : 8 : }
236 : :
237 : : static void
238 : 9 : valent_contacts_init (ValentContacts *self)
239 : : {
240 : 9 : self->adapters = g_ptr_array_new_with_free_func (g_object_unref);
241 : 9 : self->stores = g_ptr_array_new_with_free_func (g_object_unref);
242 : 9 : }
243 : :
244 : : /**
245 : : * valent_contacts_get_default:
246 : : *
247 : : * Get the default [class@Valent.Contacts].
248 : : *
249 : : * Returns: (transfer none) (not nullable): a #ValentContacts
250 : : *
251 : : * Since: 1.0
252 : : */
253 : : ValentContacts *
254 : 15 : valent_contacts_get_default (void)
255 : : {
256 [ + + ]: 15 : if (default_contacts == NULL)
257 : : {
258 : 9 : default_contacts = g_object_new (VALENT_TYPE_CONTACTS,
259 : : "plugin-domain", "contacts",
260 : : "plugin-type", VALENT_TYPE_CONTACTS_ADAPTER,
261 : : NULL);
262 : :
263 : 9 : g_object_add_weak_pointer (G_OBJECT (default_contacts),
264 : : (gpointer)&default_contacts);
265 : : }
266 : :
267 : 15 : return default_contacts;
268 : : }
269 : :
270 : : /**
271 : : * valent_contacts_ensure_store:
272 : : * @contacts: a #ValentContacts
273 : : * @uid: a unique id
274 : : * @name: a display name
275 : : *
276 : : * Get a #ValentContactStore for @uid.
277 : : *
278 : : * If the contact store does not exist, one will be created using the default
279 : : * adapter and passed @name and @description. If no adapter is available, a new
280 : : * file-based store will be created.
281 : : *
282 : : * Returns: (transfer none) (not nullable): an address book
283 : : *
284 : : * Since: 1.0
285 : : */
286 : : ValentContactStore *
287 : 8 : valent_contacts_ensure_store (ValentContacts *contacts,
288 : : const char *uid,
289 : : const char *name)
290 : : {
291 : 8 : ValentContactStore *ret;
292 : 8 : unsigned int position = 0;
293 : :
294 : 8 : VALENT_ENTRY;
295 : :
296 [ + - + - ]: 8 : g_return_val_if_fail (uid != NULL && *uid != '\0', NULL);
297 [ + - - + ]: 8 : g_return_val_if_fail (name != NULL && *name != '\0', NULL);
298 : :
299 : : /* Try to find an existing store */
300 [ + + ]: 16 : for (unsigned int i = 0, len = contacts->stores->len; i < len; i++)
301 : : {
302 : 11 : ret = g_ptr_array_index (contacts->stores, i);
303 : :
304 [ + + ]: 11 : if (g_strcmp0 (valent_contact_store_get_uid (ret), uid) == 0)
305 : 11 : VALENT_RETURN (ret);
306 : : }
307 : :
308 : : /* Create a new store */
309 : 5 : ret = valent_contacts_create_store (uid, name, NULL);
310 : 5 : position = contacts->stores->len;
311 : 5 : g_ptr_array_add (contacts->stores, ret);
312 : 5 : g_list_model_items_changed (G_LIST_MODEL (contacts), position, 0, 1);
313 : :
314 : 5 : VALENT_RETURN (ret);
315 : : }
316 : :
|