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 : : // SPDX-FileCopyrightText: Copyright 2015 Lars Uebernickel
4 : : // SPDX-FileCopyrightText: Copyright 2015 Ryan Lortie
5 : :
6 : : #define G_LOG_DOMAIN "valent-sms-store"
7 : :
8 : : #include "config.h"
9 : :
10 : : #include <gio/gio.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-message-thread.h"
14 : : #include "valent-sms-store.h"
15 : : #include "valent-sms-store-private.h"
16 : :
17 : :
18 : : struct _ValentMessageThread
19 : : {
20 : : GObject parent_instance;
21 : :
22 : : ValentSmsStore *store;
23 : : int64_t id;
24 : : GCancellable *cancellable;
25 : : GSequence *items;
26 : :
27 : : /* cache */
28 : : unsigned int last_position;
29 : : GSequenceIter *last_iter;
30 : : gboolean last_position_valid;
31 : : };
32 : :
33 : : static void g_list_model_iface_init (GListModelInterface *iface);
34 : :
35 [ + + + - ]: 20 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentMessageThread, valent_message_thread, G_TYPE_OBJECT,
36 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
37 : :
38 : : enum {
39 : : PROP_0,
40 : : PROP_STORE,
41 : : PROP_ID,
42 : : N_PROPERTIES
43 : : };
44 : :
45 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
46 : :
47 : :
48 : : #if 0
49 : : static inline int
50 : : valent_message_thread_lookup_func (gconstpointer a,
51 : : gconstpointer b,
52 : : gpointer user_data)
53 : : {
54 : : int64_t *id = user_data;
55 : :
56 : : return valent_message_get_id ((ValentMessage *)a) == *id ? 0 : 1;
57 : : }
58 : :
59 : : static inline int
60 : : valent_message_thread_sort_func (gconstpointer a,
61 : : gconstpointer b,
62 : : gpointer user_data)
63 : : {
64 : : int64_t date1 = valent_message_get_date ((ValentMessage *)a);
65 : : int64_t date2 = valent_message_get_date ((ValentMessage *)b);
66 : :
67 : : return (date1 < date2) ? -1 : (date1 > date2);
68 : : }
69 : :
70 : : static void
71 : : on_message_added (ValentSmsStore *store,
72 : : ValentMessage *message,
73 : : ValentMessageThread *self)
74 : : {
75 : : // FIXME
76 : : if (self->id == 0)
77 : : {
78 : :
79 : : }
80 : :
81 : : if (self->id == valent_message_get_thread_id (message))
82 : : {
83 : : // TODO: search for a matching id, then insert
84 : : }
85 : : }
86 : :
87 : : static void
88 : : on_message_removed (ValentSmsStore *store,
89 : : ValentMessage *message,
90 : : ValentMessageThread *self)
91 : : {
92 : : GSequenceIter *it;
93 : : unsigned int position;
94 : : int64_t id;
95 : :
96 : : if (self->id != valent_message_get_thread_id (message))
97 : : return;
98 : :
99 : : id = valent_message_get_id (message);
100 : : it = g_sequence_lookup (self->items,
101 : : &id,
102 : : valent_message_thread_lookup_func,
103 : : NULL);
104 : : position = g_sequence_iter_get_position (it);
105 : :
106 : : g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
107 : : }
108 : :
109 : : static unsigned int
110 : : valent_message_thread_add_message (ValentMessageThread *self,
111 : : ValentMessage *message)
112 : : {
113 : : GSequenceIter *it;
114 : : unsigned int position;
115 : :
116 : : g_assert (VALENT_IS_MESSAGE_THREAD (self));
117 : : g_assert (VALENT_IS_MESSAGE (message));
118 : :
119 : : it = g_sequence_insert_sorted (self->items,
120 : : g_object_ref (message),
121 : : valent_message_thread_sort_func, NULL);
122 : : position = g_sequence_iter_get_position (it);
123 : :
124 : : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
125 : :
126 : : return position;
127 : : }
128 : : #endif
129 : :
130 : : static void
131 : 5 : get_message_cb (ValentSmsStore *store,
132 : : GAsyncResult *result,
133 : : gpointer user_data)
134 : : {
135 : 10 : g_autoptr (ValentMessage) message = VALENT_MESSAGE (user_data);
136 [ + - ]: 5 : g_autoptr (ValentMessage) update = NULL;
137 [ + - ]: 5 : g_autoptr (GError) error = NULL;
138 : :
139 : 5 : update = valent_sms_store_get_message_finish (store, result, &error);
140 : :
141 [ - + ]: 5 : if (update == NULL)
142 : 0 : g_warning ("%s: %s", G_STRFUNC, error->message);
143 : : else
144 : 5 : valent_message_update (message, g_steal_pointer (&update));
145 : 5 : }
146 : :
147 : : static void
148 : 4 : get_thread_items_cb (ValentSmsStore *store,
149 : : GAsyncResult *result,
150 : : gpointer user_data)
151 : : {
152 : 4 : g_autoptr (ValentMessageThread) self = VALENT_MESSAGE_THREAD (user_data);
153 [ + - ]: 4 : g_autoptr (GPtrArray) messages = NULL;
154 : 4 : g_autoptr (GError) error = NULL;
155 : 4 : unsigned int n_items;
156 : :
157 [ - + ]: 4 : if ((messages = g_task_propagate_pointer (G_TASK (result), &error)) == NULL)
158 : : {
159 : 0 : g_warning ("%s(): loading thread %"G_GINT64_FORMAT": %s",
160 : : G_STRFUNC,
161 : : self->id,
162 : : error->message);
163 [ # # ]: 0 : return;
164 : : }
165 : :
166 : : /* Update the model */
167 : 4 : n_items = messages->len;
168 : :
169 [ + + ]: 12 : for (unsigned int i = 0; i < n_items; i++)
170 : : {
171 : 8 : ValentMessage *message;
172 : :
173 : 8 : message = g_ptr_array_index (messages, i);
174 : 8 : g_sequence_append (self->items, g_object_ref (message));
175 : : }
176 : :
177 [ - + ]: 4 : g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, n_items);
178 : : }
179 : :
180 : :
181 : : /*
182 : : * GListModel
183 : : */
184 : : static GType
185 : 1 : valent_message_thread_get_item_type (GListModel *model)
186 : : {
187 : 1 : return VALENT_TYPE_MESSAGE;
188 : : }
189 : :
190 : : static unsigned int
191 : 3 : valent_message_thread_get_n_items (GListModel *model)
192 : : {
193 : 3 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (model);
194 : :
195 : 3 : return g_sequence_get_length (self->items);
196 : : }
197 : :
198 : : static gpointer
199 : 5 : valent_message_thread_get_item (GListModel *model,
200 : : unsigned int position)
201 : : {
202 : 5 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (model);
203 : 5 : ValentMessage *message = NULL;
204 : 5 : GSequenceIter *it = NULL;
205 : :
206 [ + + ]: 5 : if (self->last_position_valid)
207 : : {
208 [ + - + - ]: 2 : if (position < G_MAXUINT && self->last_position == position + 1)
209 : 2 : it = g_sequence_iter_prev (self->last_iter);
210 [ # # # # ]: 0 : else if (position > 0 && self->last_position == position - 1)
211 : 0 : it = g_sequence_iter_next (self->last_iter);
212 [ # # ]: 0 : else if (self->last_position == position)
213 : 0 : it = self->last_iter;
214 : : }
215 : :
216 [ - + ]: 2 : if (it == NULL)
217 : 3 : it = g_sequence_get_iter_at_pos (self->items, position);
218 : :
219 : 5 : self->last_iter = it;
220 : 5 : self->last_position = position;
221 : 5 : self->last_position_valid = TRUE;
222 : :
223 [ + - ]: 5 : if (g_sequence_iter_is_end (it))
224 : : return NULL;
225 : :
226 : 5 : message = g_object_ref (g_sequence_get (it));
227 : :
228 : : /* Lazy fetch */
229 [ + - ]: 5 : if (valent_message_get_box (message) == 0)
230 : : {
231 : 5 : valent_sms_store_get_message (self->store,
232 : : valent_message_get_id (message),
233 : : self->cancellable,
234 : : (GAsyncReadyCallback)get_message_cb,
235 : : g_object_ref (message));
236 : : }
237 : :
238 : : return message;
239 : : }
240 : :
241 : : static void
242 : 4 : g_list_model_iface_init (GListModelInterface *iface)
243 : : {
244 : 4 : iface->get_item = valent_message_thread_get_item;
245 : 4 : iface->get_item_type = valent_message_thread_get_item_type;
246 : 4 : iface->get_n_items = valent_message_thread_get_n_items;
247 : 4 : }
248 : :
249 : :
250 : : /*
251 : : * GObject
252 : : */
253 : : static void
254 : 4 : valent_message_thread_dispose (GObject *object)
255 : : {
256 : 4 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
257 : :
258 : 4 : g_cancellable_cancel (self->cancellable);
259 : :
260 : 4 : G_OBJECT_CLASS (valent_message_thread_parent_class)->dispose (object);
261 : 4 : }
262 : :
263 : : static void
264 : 4 : valent_message_thread_finalize (GObject *object)
265 : : {
266 : 4 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
267 : :
268 [ + - ]: 4 : g_clear_object (&self->store);
269 [ + - ]: 4 : g_clear_pointer (&self->items, g_sequence_free);
270 [ + - ]: 4 : g_clear_object (&self->cancellable);
271 : :
272 : 4 : G_OBJECT_CLASS (valent_message_thread_parent_class)->finalize (object);
273 : 4 : }
274 : :
275 : : static void
276 : 2 : valent_message_thread_get_property (GObject *object,
277 : : guint prop_id,
278 : : GValue *value,
279 : : GParamSpec *pspec)
280 : : {
281 : 2 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
282 : :
283 [ + + - ]: 2 : switch (prop_id)
284 : : {
285 : 1 : case PROP_ID:
286 : 1 : g_value_set_int64 (value, self->id);
287 : 1 : break;
288 : :
289 : 1 : case PROP_STORE:
290 : 1 : g_value_set_object (value, self->store);
291 : 1 : break;
292 : :
293 : 0 : default:
294 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
295 : : }
296 : 2 : }
297 : :
298 : : static void
299 : 8 : valent_message_thread_set_property (GObject *object,
300 : : guint prop_id,
301 : : const GValue *value,
302 : : GParamSpec *pspec)
303 : : {
304 : 8 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
305 : :
306 [ + + - ]: 8 : switch (prop_id)
307 : : {
308 : 4 : case PROP_ID:
309 : 4 : valent_message_thread_set_id (self, g_value_get_int64 (value));
310 : 4 : break;
311 : :
312 : 4 : case PROP_STORE:
313 : 4 : self->store = g_value_dup_object (value);
314 : 4 : break;
315 : :
316 : 0 : default:
317 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
318 : : }
319 : 8 : }
320 : :
321 : : static void
322 : 4 : valent_message_thread_class_init (ValentMessageThreadClass *klass)
323 : : {
324 : 4 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
325 : :
326 : 4 : object_class->dispose = valent_message_thread_dispose;
327 : 4 : object_class->finalize = valent_message_thread_finalize;
328 : 4 : object_class->get_property = valent_message_thread_get_property;
329 : 4 : object_class->set_property = valent_message_thread_set_property;
330 : :
331 : : /**
332 : : * ValentMessageThread:id:
333 : : *
334 : : * The ID of the thread.
335 : : */
336 : 8 : properties [PROP_ID] =
337 : 4 : g_param_spec_int64 ("id", NULL, NULL,
338 : : G_MININT64, G_MAXINT64,
339 : : 0,
340 : : (G_PARAM_READWRITE |
341 : : G_PARAM_CONSTRUCT |
342 : : G_PARAM_EXPLICIT_NOTIFY |
343 : : G_PARAM_STATIC_STRINGS));
344 : :
345 : : /**
346 : : * ValentMessageThread:message-store:
347 : : *
348 : : * The `ValentSmsStore` providing `ValentMessage` objects for the thread.
349 : : */
350 : 8 : properties [PROP_STORE] =
351 : 4 : g_param_spec_object ("store", NULL, NULL,
352 : : VALENT_TYPE_SMS_STORE,
353 : : (G_PARAM_READWRITE |
354 : : G_PARAM_CONSTRUCT_ONLY |
355 : : G_PARAM_EXPLICIT_NOTIFY |
356 : : G_PARAM_STATIC_STRINGS));
357 : :
358 : 4 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
359 : 4 : }
360 : :
361 : : static void
362 : 4 : valent_message_thread_init (ValentMessageThread *self)
363 : : {
364 : 4 : self->cancellable = g_cancellable_new ();
365 : 4 : self->items = g_sequence_new (g_object_unref);
366 : 4 : }
367 : :
368 : : /**
369 : : * valent_message_thread_new:
370 : : * @store: a @ValentSmsStore
371 : : * @id: a thread ID
372 : : *
373 : : * Create a new `ValentMessageThread`.
374 : : *
375 : : * Returns: (transfer full): a `GListModel`
376 : : */
377 : : GListModel *
378 : 4 : valent_message_thread_new (ValentSmsStore *store,
379 : : int64_t id)
380 : : {
381 [ + - ]: 4 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), NULL);
382 [ - + ]: 4 : g_return_val_if_fail (id >= 0, NULL);
383 : :
384 : 4 : return g_object_new (VALENT_TYPE_MESSAGE_THREAD,
385 : : "id", id,
386 : : "store", store,
387 : : NULL);
388 : : }
389 : :
390 : : /**
391 : : * valent_message_thread_get_id:
392 : : * @thread: a `ValentMessageThread`
393 : : *
394 : : * Get the thread ID for @thread.
395 : : *
396 : : * Returns: an ID
397 : : */
398 : : int64_t
399 : 1 : valent_message_thread_get_id (ValentMessageThread *thread)
400 : : {
401 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_MESSAGE_THREAD (thread), 0);
402 : :
403 : 1 : return thread->id;
404 : : }
405 : :
406 : : /**
407 : : * valent_message_thread_set_id:
408 : : * @thread: a `ValentMessageThread`
409 : : * @id: a thread ID
410 : : *
411 : : * Set the thread ID for @thread to @id.
412 : : */
413 : : void
414 : 4 : valent_message_thread_set_id (ValentMessageThread *thread,
415 : : int64_t id)
416 : : {
417 [ + - ]: 4 : g_return_if_fail (VALENT_IS_MESSAGE_THREAD (thread));
418 [ - + ]: 4 : g_return_if_fail (id >= 0);
419 : :
420 [ + - ]: 4 : if (thread->id == id)
421 : : return;
422 : :
423 : 4 : thread->id = id;
424 : 4 : g_object_notify_by_pspec (G_OBJECT (thread), properties [PROP_ID]);
425 : :
426 : : /* Load the thread items, while holding a reference to the thread */
427 : 4 : valent_sms_store_get_thread_items (thread->store,
428 : : thread->id,
429 : : thread->cancellable,
430 : : (GAsyncReadyCallback)get_thread_items_cb,
431 : : g_object_ref (thread));
432 : : }
433 : :
434 : : /**
435 : : * valent_message_thread_get_db:
436 : : * @thread: a `ValentMessageThread`
437 : : *
438 : : * Get the thread ID for @thread.
439 : : *
440 : : * Returns: a `ValentSmsStore`
441 : : */
442 : : ValentSmsStore *
443 : 1 : valent_message_thread_get_store (ValentMessageThread *thread)
444 : : {
445 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_MESSAGE_THREAD (thread), NULL);
446 : :
447 : 1 : return thread->store;
448 : : }
449 : :
|