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 Alison Karlitskya
4 : : // SPDX-FileCopyrightText: Copyright 2015 Lars Uebernickel
5 : :
6 : : #define G_LOG_DOMAIN "valent-message-thread"
7 : :
8 : : #include "config.h"
9 : :
10 : : #include <gio/gio.h>
11 : : #include <libtracker-sparql/tracker-sparql.h>
12 : : #include <libvalent-core.h>
13 : :
14 : : #include "valent-messages.h"
15 : : #include "valent-messages-adapter-private.h"
16 : :
17 : : #include "valent-message-thread.h"
18 : :
19 : : #define GET_MESSAGE_RQ "/ca/andyholmes/Valent/sparql/get-message.rq"
20 : : #define GET_THREAD_MESSAGES_RQ "/ca/andyholmes/Valent/sparql/get-thread-messages.rq"
21 : :
22 : : struct _ValentMessageThread
23 : : {
24 : : ValentResource parent_instance;
25 : :
26 : : TrackerSparqlConnection *connection;
27 : : char *iri;
28 : : ValentMessage *latest_message;
29 : : GStrv participants;
30 : :
31 : : TrackerNotifier *notifier;
32 : : GRegex *iri_pattern;
33 : : TrackerSparqlStatement *get_message_stmt;
34 : : TrackerSparqlStatement *get_thread_messages_stmt;
35 : : GCancellable *cancellable;
36 : :
37 : : /* list */
38 : : GSequence *items;
39 : : unsigned int last_position;
40 : : GSequenceIter *last_iter;
41 : : gboolean last_position_valid;
42 : : };
43 : :
44 : : static void g_list_model_iface_init (GListModelInterface *iface);
45 : :
46 : : static void valent_message_thread_load (ValentMessageThread *self);
47 : : static void valent_message_thread_load_message (ValentMessageThread *self,
48 : : const char *iri);
49 : :
50 [ + + + - ]: 49 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentMessageThread, valent_message_thread, VALENT_TYPE_RESOURCE,
51 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
52 : :
53 : : typedef enum {
54 : : PROP_CONNECTION = 1,
55 : : PROP_LATEST_MESSAGE,
56 : : PROP_NOTIFIER,
57 : : PROP_PARTICIPANTS,
58 : : } ValentMessageThreadProperty;
59 : :
60 : : static GParamSpec *properties[PROP_PARTICIPANTS + 1] = { NULL, };
61 : :
62 : : static void
63 : 9 : valent_message_thread_items_changed (ValentMessageThread *self,
64 : : unsigned int position,
65 : : unsigned int removed,
66 : : unsigned int added)
67 : : {
68 : : /* check if the iter cache may have been invalidated */
69 [ + + ]: 9 : if (position <= self->last_position)
70 : : {
71 : 5 : self->last_iter = NULL;
72 : 5 : self->last_position = 0;
73 : 5 : self->last_position_valid = FALSE;
74 : : }
75 : :
76 : 9 : g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
77 : 9 : }
78 : :
79 : : static inline int
80 : 4 : valent_message_thread_sort_func (gconstpointer a,
81 : : gconstpointer b,
82 : : gpointer user_data)
83 : : {
84 : 4 : int64_t date1 = valent_message_get_date ((ValentMessage *)a);
85 : 4 : int64_t date2 = valent_message_get_date ((ValentMessage *)b);
86 : :
87 [ + + ]: 4 : return (date1 < date2) ? -1 : (date1 > date2);
88 : : }
89 : :
90 : : static void
91 : 7 : valent_message_thread_load_message_cb (GObject *object,
92 : : GAsyncResult *result,
93 : : gpointer user_data)
94 : : {
95 : 7 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
96 : 7 : g_autoptr (ValentMessage) message = NULL;
97 : 7 : int64_t latest_date = 0;
98 : 7 : GSequenceIter *it;
99 : 7 : unsigned int position;
100 [ + - ]: 7 : g_autoptr (GError) error = NULL;
101 : :
102 : 7 : message = g_task_propagate_pointer (G_TASK (result), &error);
103 [ - + ]: 7 : if (message == NULL)
104 : : {
105 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
106 : : {
107 : 0 : const char *urn = g_task_get_task_data (G_TASK (result));
108 : 0 : g_warning ("%s(): %s: %s", G_STRFUNC, urn, error->message);
109 : : }
110 : :
111 : 0 : return;
112 : : }
113 : :
114 [ + - ]: 7 : if (self->latest_message != NULL)
115 : 7 : latest_date = valent_message_get_date (self->latest_message);
116 : :
117 [ + + ]: 7 : if (valent_message_get_date (message) > latest_date)
118 : : {
119 : 5 : g_set_object (&self->latest_message, message);
120 : 5 : g_object_notify_by_pspec (G_OBJECT (self),
121 : : properties[PROP_LATEST_MESSAGE]);
122 : : }
123 : :
124 : : /* Bail if we haven't loaded the rest of the thread yet
125 : : */
126 [ + + ]: 7 : if (self->cancellable == NULL)
127 : : return;
128 : :
129 : 4 : it = g_sequence_insert_sorted (self->items,
130 : : g_object_ref (message),
131 : : valent_message_thread_sort_func, NULL);
132 : :
133 : 4 : position = g_sequence_iter_get_position (it);
134 [ - + ]: 4 : valent_message_thread_items_changed (self, position, 0, 1);
135 : : }
136 : :
137 : : static inline int
138 : 2 : valent_message_thread_lookup_func (gconstpointer a,
139 : : gconstpointer b,
140 : : gpointer user_data)
141 : : {
142 : 2 : const char *iri = valent_resource_get_iri ((ValentResource *)a);
143 : :
144 : 2 : return g_utf8_collate (iri, (const char *)b);
145 : : }
146 : :
147 : : static void
148 : 2 : valent_message_thread_remove_message (ValentMessageThread *self,
149 : : const char *iri)
150 : : {
151 : 2 : GSequenceIter *it;
152 : 2 : unsigned int position;
153 : :
154 [ + - ]: 2 : g_assert (VALENT_IS_MESSAGE_THREAD (self));
155 : :
156 : 2 : it = g_sequence_lookup (self->items,
157 : : (char *)iri,
158 : : valent_message_thread_lookup_func,
159 : : NULL);
160 : :
161 [ + + ]: 2 : if (it != NULL)
162 : : {
163 : 1 : position = g_sequence_iter_get_position (it);
164 : 1 : g_sequence_remove (it);
165 : 1 : valent_message_thread_items_changed (self, position, 1, 0);
166 : : }
167 : 2 : }
168 : :
169 : : gboolean
170 : 34 : valent_message_thread_event_is_message (ValentMessageThread *self,
171 : : const char *iri)
172 : : {
173 : 0 : return g_regex_match (self->iri_pattern, iri, G_REGEX_MATCH_DEFAULT, NULL);
174 : : }
175 : :
176 : : static void
177 : 14 : on_notifier_event (TrackerNotifier *notifier,
178 : : const char *service,
179 : : const char *graph,
180 : : GPtrArray *events,
181 : : ValentMessageThread *self)
182 : : {
183 : 14 : const char *latest_urn = NULL;
184 : :
185 [ + - ]: 14 : g_assert (VALENT_IS_MESSAGE_THREAD (self));
186 : :
187 [ + - ]: 14 : if (g_strcmp0 (VALENT_MESSAGES_GRAPH, graph) != 0)
188 : : return;
189 : :
190 [ + + ]: 48 : for (unsigned int i = 0; i < events->len; i++)
191 : : {
192 : 34 : TrackerNotifierEvent *event = g_ptr_array_index (events, i);
193 : 34 : const char *urn = tracker_notifier_event_get_urn (event);
194 : :
195 [ + + ]: 34 : if (!valent_message_thread_event_is_message (self, urn))
196 : 18 : continue;
197 : :
198 [ + + + - ]: 16 : switch (tracker_notifier_event_get_event_type (event))
199 : : {
200 : 8 : case TRACKER_NOTIFIER_EVENT_CREATE:
201 : 8 : VALENT_NOTE ("CREATE: %s", urn);
202 : : // HACK: if the thread hasn't been loaded, assume newer messages sort
203 : : // last and pick one to update the last-message.
204 [ + + ]: 8 : if (self->cancellable != NULL)
205 : 4 : valent_message_thread_load_message (self, urn);
206 [ - + - - ]: 4 : else if (latest_urn == NULL || g_utf8_collate (latest_urn, urn) < 0)
207 : 34 : latest_urn = urn;
208 : : break;
209 : :
210 : 2 : case TRACKER_NOTIFIER_EVENT_DELETE:
211 : 2 : VALENT_NOTE ("DELETE: %s", urn);
212 : 2 : valent_message_thread_remove_message (self, urn);
213 : 2 : break;
214 : :
215 : : case TRACKER_NOTIFIER_EVENT_UPDATE:
216 : : VALENT_NOTE ("UPDATE: %s", urn);
217 : : // valent_message_thread_update_message (self, urn);
218 : : break;
219 : :
220 : 0 : default:
221 : 34 : g_warn_if_reached ();
222 : : }
223 : : }
224 : :
225 [ + + ]: 14 : if (latest_urn != NULL)
226 : 4 : valent_message_thread_load_message (self, latest_urn);
227 : : }
228 : :
229 : : static void
230 : 15 : cursor_get_message_cb (TrackerSparqlCursor *cursor,
231 : : GAsyncResult *result,
232 : : gpointer user_data)
233 : : {
234 : 30 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
235 : 15 : ValentMessage *current = g_task_get_task_data (task);
236 [ + - + - ]: 15 : g_autoptr (GError) error = NULL;
237 : :
238 [ + + ]: 15 : if (tracker_sparql_cursor_next_finish (cursor, result, &error))
239 : : {
240 [ - + ]: 8 : g_autoptr (ValentMessage) message = NULL;
241 : :
242 : 8 : message = valent_message_from_sparql_cursor (cursor, current);
243 [ + - ]: 8 : if (message != current)
244 : 8 : g_task_set_task_data (task, g_steal_pointer (&message), g_object_unref);
245 : :
246 : 8 : tracker_sparql_cursor_next_async (cursor,
247 : : g_task_get_cancellable (task),
248 : : (GAsyncReadyCallback) cursor_get_message_cb,
249 : : g_object_ref (task));
250 [ - + ]: 8 : return;
251 : : }
252 : :
253 [ + - ]: 7 : if (current != NULL)
254 : : {
255 : 7 : g_task_return_pointer (task, g_object_ref (current), g_object_unref);
256 : : }
257 : : else
258 : : {
259 [ # # ]: 0 : if (error == NULL)
260 : : {
261 : 0 : g_set_error_literal (&error,
262 : : G_IO_ERROR,
263 : : G_IO_ERROR_NOT_FOUND,
264 : : "Failed to find message");
265 : : }
266 : :
267 : 0 : g_task_return_error (task, g_steal_pointer (&error));
268 : : }
269 : :
270 [ - + ]: 7 : tracker_sparql_cursor_close (cursor);
271 : : }
272 : :
273 : : static void
274 : 8 : execute_get_message_cb (TrackerSparqlStatement *stmt,
275 : : GAsyncResult *result,
276 : : gpointer user_data)
277 : : {
278 : 16 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
279 : 8 : GCancellable *cancellable = g_task_get_cancellable (task);
280 [ - - + - ]: 8 : g_autoptr (TrackerSparqlCursor) cursor = NULL;
281 : 8 : GError *error = NULL;
282 : :
283 : 8 : cursor = tracker_sparql_statement_execute_finish (stmt, result, &error);
284 [ - + ]: 8 : if (cursor == NULL)
285 : : {
286 : 0 : g_task_return_error (task, g_steal_pointer (&error));
287 [ # # ]: 0 : return;
288 : : }
289 : :
290 : 8 : tracker_sparql_cursor_next_async (cursor,
291 : : cancellable,
292 : : (GAsyncReadyCallback) cursor_get_message_cb,
293 : : g_object_ref (task));
294 : : }
295 : :
296 : : static void
297 : 8 : valent_message_thread_load_message (ValentMessageThread *self,
298 : : const char *iri)
299 : : {
300 : 8 : g_autoptr (GTask) task = NULL;
301 : 8 : GError *error = NULL;
302 : :
303 [ + - ]: 8 : g_assert (VALENT_IS_MESSAGE_THREAD (self));
304 [ - + ]: 8 : g_assert (iri != NULL);
305 : :
306 : 8 : task = g_task_new (self, self->cancellable, valent_message_thread_load_message_cb, NULL);
307 [ + - ]: 8 : g_task_set_source_tag (task, valent_message_thread_load_message);
308 : :
309 [ + - ]: 8 : if (self->get_message_stmt == NULL)
310 : : {
311 : 8 : self->get_message_stmt =
312 : 8 : tracker_sparql_connection_load_statement_from_gresource (self->connection,
313 : : GET_MESSAGE_RQ,
314 : : self->cancellable,
315 : : &error);
316 : : }
317 : :
318 [ - + ]: 8 : if (self->get_message_stmt == NULL)
319 : : {
320 : 0 : g_task_return_error (task, g_steal_pointer (&error));
321 [ # # ]: 0 : return;
322 : : }
323 : :
324 : 8 : tracker_sparql_statement_bind_string (self->get_message_stmt, "iri", iri);
325 [ + - ]: 8 : tracker_sparql_statement_execute_async (self->get_message_stmt,
326 : : self->cancellable,
327 : : (GAsyncReadyCallback) execute_get_message_cb,
328 : : g_object_ref (task));
329 : : }
330 : :
331 : : static void
332 : 4 : valent_message_thread_load_cb (GObject *object,
333 : : GAsyncResult *result,
334 : : gpointer user_data)
335 : : {
336 : 4 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
337 : 4 : g_autoptr (GPtrArray) messages = NULL;
338 : 4 : g_autoptr (GError) error = NULL;
339 : 4 : unsigned int position = 0;
340 : :
341 : 4 : messages = g_task_propagate_pointer (G_TASK (result), &error);
342 [ - + ]: 4 : if (messages == NULL)
343 : : {
344 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
345 : 0 : g_warning ("%s(): %s: %s", G_STRFUNC, self->iri, error->message);
346 : :
347 [ # # ]: 0 : return;
348 : : }
349 : :
350 : 4 : position = g_sequence_get_length (self->items);
351 [ + + ]: 9 : for (unsigned int i = 0; i < messages->len; i++)
352 : 5 : g_sequence_append (self->items, g_object_ref (g_ptr_array_index (messages, i)));
353 : :
354 [ - + ]: 4 : valent_message_thread_items_changed (self, position, 0, messages->len);
355 : : }
356 : :
357 : : static void
358 : 9 : cursor_get_messages_cb (TrackerSparqlCursor *cursor,
359 : : GAsyncResult *result,
360 : : gpointer user_data)
361 : : {
362 : 9 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
363 : 9 : GPtrArray *messages = g_task_get_task_data (task);
364 : 9 : GError *error = NULL;
365 : :
366 [ + + ]: 9 : if (tracker_sparql_cursor_next_finish (cursor, result, &error))
367 : : {
368 : 5 : ValentMessage *current = NULL;
369 [ + - ]: 9 : g_autoptr (ValentMessage) message = NULL;
370 : :
371 [ + + ]: 5 : if (messages->len > 0)
372 : 1 : current = g_ptr_array_index (messages, messages->len - 1);
373 : :
374 : 5 : message = valent_message_from_sparql_cursor (cursor, current);
375 [ + - ]: 5 : if (message != current)
376 : 5 : g_ptr_array_add (messages, g_steal_pointer (&message));
377 : :
378 [ - + ]: 5 : tracker_sparql_cursor_next_async (cursor,
379 : : g_task_get_cancellable (task),
380 : : (GAsyncReadyCallback) cursor_get_messages_cb,
381 : : g_object_ref (task));
382 : : }
383 [ + - ]: 4 : else if (error == NULL)
384 : : {
385 : 4 : g_task_return_pointer (task,
386 : 4 : g_ptr_array_ref (messages),
387 : : (GDestroyNotify)g_ptr_array_unref);
388 : 4 : tracker_sparql_cursor_close (cursor);
389 : : }
390 : : else
391 : : {
392 : 0 : g_task_return_error (task, g_steal_pointer (&error));
393 : 0 : tracker_sparql_cursor_close (cursor);
394 : : }
395 : 9 : }
396 : :
397 : : static void
398 : 4 : execute_get_messages_cb (TrackerSparqlStatement *stmt,
399 : : GAsyncResult *result,
400 : : gpointer user_data)
401 : : {
402 : 8 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
403 : 4 : GCancellable *cancellable = g_task_get_cancellable (task);
404 [ - - + - ]: 4 : g_autoptr (TrackerSparqlCursor) cursor = NULL;
405 : 4 : GError *error = NULL;
406 : :
407 : 4 : cursor = tracker_sparql_statement_execute_finish (stmt, result, &error);
408 [ - + ]: 4 : if (cursor == NULL)
409 : : {
410 : 0 : g_task_return_error (task, g_steal_pointer (&error));
411 [ # # ]: 0 : return;
412 : : }
413 : :
414 : 4 : tracker_sparql_cursor_next_async (cursor,
415 : : cancellable,
416 : : (GAsyncReadyCallback) cursor_get_messages_cb,
417 : : g_object_ref (task));
418 : : }
419 : :
420 : : static void
421 : 4 : valent_message_thread_load (ValentMessageThread *self)
422 : : {
423 : 4 : g_autoptr (GTask) task = NULL;
424 : 4 : GError *error = NULL;
425 : :
426 [ + - ]: 4 : g_assert (VALENT_IS_MESSAGE_THREAD (self));
427 : :
428 [ + - + - ]: 4 : if (self->connection == NULL || self->cancellable != NULL)
429 : : return;
430 : :
431 : 4 : self->cancellable = valent_object_ref_cancellable (VALENT_OBJECT (self));
432 : 4 : task = g_task_new (self, self->cancellable, valent_message_thread_load_cb, NULL);
433 [ + - ]: 4 : g_task_set_source_tag (task, valent_message_thread_load);
434 : 4 : g_task_set_task_data (task,
435 : 4 : g_ptr_array_new_with_free_func (g_object_unref),
436 : : (GDestroyNotify)g_ptr_array_unref);
437 : :
438 [ + - ]: 4 : if (self->get_thread_messages_stmt == NULL)
439 : : {
440 : 4 : self->get_thread_messages_stmt =
441 : 4 : tracker_sparql_connection_load_statement_from_gresource (self->connection,
442 : : GET_THREAD_MESSAGES_RQ,
443 : : self->cancellable,
444 : : &error);
445 : : }
446 : :
447 [ - + ]: 4 : if (self->get_thread_messages_stmt == NULL)
448 : : {
449 : 0 : g_task_return_error (task, g_steal_pointer (&error));
450 [ # # ]: 0 : return;
451 : : }
452 : :
453 : 4 : tracker_sparql_statement_bind_string (self->get_thread_messages_stmt, "iri",
454 : 4 : self->iri);
455 [ + - ]: 4 : tracker_sparql_statement_execute_async (self->get_thread_messages_stmt,
456 : : g_task_get_cancellable (task),
457 : : (GAsyncReadyCallback) execute_get_messages_cb,
458 : : g_object_ref (task));
459 : : }
460 : :
461 : : /*
462 : : * GListModel
463 : : */
464 : : static GType
465 : 1 : valent_message_thread_get_item_type (GListModel *model)
466 : : {
467 : 1 : return VALENT_TYPE_MESSAGE;
468 : : }
469 : :
470 : : static unsigned int
471 : 2998 : valent_message_thread_get_n_items (GListModel *model)
472 : : {
473 : 2998 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (model);
474 : :
475 [ + + ]: 2998 : if (self->cancellable == NULL)
476 : 4 : valent_message_thread_load (self);
477 : :
478 : 2998 : return g_sequence_get_length (self->items);
479 : : }
480 : :
481 : : static gpointer
482 : 5 : valent_message_thread_get_item (GListModel *model,
483 : : unsigned int position)
484 : : {
485 : 5 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (model);
486 : 5 : GSequenceIter *it = NULL;
487 : :
488 [ + + ]: 5 : if (self->last_position_valid)
489 : : {
490 [ + - - + ]: 1 : if (position < G_MAXUINT && self->last_position == position + 1)
491 : 0 : it = g_sequence_iter_prev (self->last_iter);
492 [ + - + - ]: 1 : else if (position > 0 && self->last_position == position - 1)
493 : 1 : it = g_sequence_iter_next (self->last_iter);
494 [ # # ]: 0 : else if (self->last_position == position)
495 : 0 : it = self->last_iter;
496 : : }
497 : :
498 [ - + ]: 1 : if (it == NULL)
499 : 4 : it = g_sequence_get_iter_at_pos (self->items, position);
500 : :
501 : 5 : self->last_iter = it;
502 : 5 : self->last_position = position;
503 : 5 : self->last_position_valid = TRUE;
504 : :
505 [ + - ]: 5 : if (g_sequence_iter_is_end (it))
506 : : return NULL;
507 : :
508 : 5 : return g_object_ref (g_sequence_get (it));
509 : : }
510 : :
511 : : static void
512 : 3 : g_list_model_iface_init (GListModelInterface *iface)
513 : : {
514 : 3 : iface->get_item = valent_message_thread_get_item;
515 : 3 : iface->get_item_type = valent_message_thread_get_item_type;
516 : 3 : iface->get_n_items = valent_message_thread_get_n_items;
517 : 3 : }
518 : :
519 : : /*
520 : : * GObject
521 : : */
522 : : static void
523 : 15 : valent_message_thread_constructed (GObject *object)
524 : : {
525 : 15 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
526 : :
527 : 15 : G_OBJECT_CLASS (valent_message_thread_parent_class)->constructed (object);
528 : :
529 : 15 : g_object_get (VALENT_OBJECT (self), "iri", &self->iri, NULL);
530 : :
531 [ + - - + ]: 15 : if (self->connection != NULL && self->notifier == NULL)
532 : 0 : self->notifier = tracker_sparql_connection_create_notifier (self->connection);
533 : :
534 [ + - ]: 15 : if (self->notifier != NULL)
535 : : {
536 : 15 : g_autofree char *iri_pattern = NULL;
537 : :
538 : 15 : g_signal_connect_object (self->notifier,
539 : : "events",
540 : : G_CALLBACK (on_notifier_event),
541 : : self,
542 : : G_CONNECT_DEFAULT);
543 : :
544 : 15 : iri_pattern = g_strdup_printf ("^%s:([^:]+)$", self->iri);
545 : 15 : self->iri_pattern = g_regex_new (iri_pattern,
546 : : G_REGEX_OPTIMIZE,
547 : : G_REGEX_MATCH_DEFAULT,
548 : : NULL);
549 : : }
550 : 15 : }
551 : :
552 : : static void
553 : 12 : valent_message_thread_destroy (ValentObject *object)
554 : : {
555 : 12 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
556 : :
557 [ + + ]: 12 : g_clear_object (&self->get_message_stmt);
558 [ + + ]: 12 : g_clear_object (&self->get_thread_messages_stmt);
559 [ + - ]: 12 : g_clear_pointer (&self->iri_pattern, g_regex_unref);
560 [ + - ]: 12 : g_clear_pointer (&self->iri, g_free);
561 : :
562 [ + - ]: 12 : if (self->notifier != NULL)
563 : : {
564 : 12 : g_signal_handlers_disconnect_by_func (self->notifier, on_notifier_event, self);
565 [ + - ]: 12 : g_clear_object (&self->notifier);
566 : : }
567 [ + - ]: 12 : g_clear_object (&self->connection);
568 : :
569 : 12 : VALENT_OBJECT_CLASS (valent_message_thread_parent_class)->destroy (object);
570 : 12 : }
571 : :
572 : : static void
573 : 12 : valent_message_thread_finalize (GObject *object)
574 : : {
575 : 12 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
576 : :
577 [ + - ]: 12 : g_clear_object (&self->latest_message);
578 [ + - ]: 12 : g_clear_pointer (&self->participants, g_strfreev);
579 [ + + ]: 12 : g_clear_object (&self->cancellable);
580 [ + - ]: 12 : g_clear_pointer (&self->items, g_sequence_free);
581 : :
582 : 12 : G_OBJECT_CLASS (valent_message_thread_parent_class)->finalize (object);
583 : 12 : }
584 : :
585 : : static void
586 : 14 : valent_message_thread_get_property (GObject *object,
587 : : guint prop_id,
588 : : GValue *value,
589 : : GParamSpec *pspec)
590 : : {
591 : 14 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
592 : :
593 [ + + - + : 14 : switch ((ValentMessageThreadProperty)prop_id)
- ]
594 : : {
595 : 1 : case PROP_CONNECTION:
596 : 1 : g_value_set_object (value, self->connection);
597 : 1 : break;
598 : :
599 : 7 : case PROP_LATEST_MESSAGE:
600 : 7 : g_value_set_object (value, self->latest_message);
601 : 7 : break;
602 : :
603 : 0 : case PROP_NOTIFIER:
604 : 0 : g_value_set_object (value, self->notifier);
605 : 0 : break;
606 : :
607 : 6 : case PROP_PARTICIPANTS:
608 : 6 : g_value_set_boxed (value, self->participants);
609 : 6 : break;
610 : :
611 : 0 : default:
612 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
613 : : }
614 : 14 : }
615 : :
616 : : static void
617 : 60 : valent_message_thread_set_property (GObject *object,
618 : : guint prop_id,
619 : : const GValue *value,
620 : : GParamSpec *pspec)
621 : : {
622 : 60 : ValentMessageThread *self = VALENT_MESSAGE_THREAD (object);
623 : :
624 [ + + + + : 60 : switch ((ValentMessageThreadProperty)prop_id)
- ]
625 : : {
626 : 15 : case PROP_CONNECTION:
627 : 15 : self->connection = g_value_dup_object (value);
628 : 15 : break;
629 : :
630 : 15 : case PROP_LATEST_MESSAGE:
631 [ + - ]: 15 : g_assert (self->latest_message == NULL);
632 : 15 : self->latest_message = g_value_dup_object (value);
633 : 15 : break;
634 : :
635 : 15 : case PROP_NOTIFIER:
636 : 15 : self->notifier = g_value_dup_object (value);
637 : 15 : break;
638 : :
639 : 15 : case PROP_PARTICIPANTS:
640 [ - + ]: 15 : g_assert (self->participants == NULL);
641 : 15 : self->participants = g_value_dup_boxed (value);
642 : 15 : break;
643 : :
644 : 0 : default:
645 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
646 : : }
647 : 60 : }
648 : :
649 : : static void
650 : 3 : valent_message_thread_class_init (ValentMessageThreadClass *klass)
651 : : {
652 : 3 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
653 : 3 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
654 : :
655 : 3 : object_class->constructed = valent_message_thread_constructed;
656 : 3 : object_class->finalize = valent_message_thread_finalize;
657 : 3 : object_class->get_property = valent_message_thread_get_property;
658 : 3 : object_class->set_property = valent_message_thread_set_property;
659 : :
660 : 3 : vobject_class->destroy = valent_message_thread_destroy;
661 : :
662 : : /**
663 : : * ValentMessageThread:connection:
664 : : *
665 : : * The [class@Tsparql.SparqlConnection] to the graph.
666 : : */
667 : 6 : properties [PROP_CONNECTION] =
668 : 3 : g_param_spec_object ("connection", NULL, NULL,
669 : : TRACKER_TYPE_SPARQL_CONNECTION,
670 : : (G_PARAM_READWRITE |
671 : : G_PARAM_CONSTRUCT_ONLY |
672 : : G_PARAM_EXPLICIT_NOTIFY |
673 : : G_PARAM_STATIC_STRINGS));
674 : :
675 : : /**
676 : : * ValentMessageThread:latest-message:
677 : : *
678 : : * The `ValentMessagesAdapter` providing `ValentMessage` objects for the thread.
679 : : */
680 : 6 : properties [PROP_LATEST_MESSAGE] =
681 : 3 : g_param_spec_object ("latest-message", NULL, NULL,
682 : : VALENT_TYPE_MESSAGE,
683 : : (G_PARAM_READWRITE |
684 : : G_PARAM_CONSTRUCT_ONLY |
685 : : G_PARAM_EXPLICIT_NOTIFY |
686 : : G_PARAM_STATIC_STRINGS));
687 : :
688 : : /**
689 : : * ValentMessageThread:notifier:
690 : : *
691 : : * The [class@Tsparql.Notifier] watching the graph.
692 : : */
693 : 6 : properties [PROP_NOTIFIER] =
694 : 3 : g_param_spec_object ("notifier", NULL, NULL,
695 : : TRACKER_TYPE_NOTIFIER,
696 : : (G_PARAM_READWRITE |
697 : : G_PARAM_CONSTRUCT_ONLY |
698 : : G_PARAM_EXPLICIT_NOTIFY |
699 : : G_PARAM_STATIC_STRINGS));
700 : :
701 : : /**
702 : : * ValentMessageThread:participants:
703 : : *
704 : : * A list of contact mediums (i.e. phone number, email) involved in the
705 : : * thread.
706 : : */
707 : 6 : properties [PROP_PARTICIPANTS] =
708 : 3 : g_param_spec_boxed ("participants", NULL, NULL,
709 : : G_TYPE_STRV,
710 : : (G_PARAM_READWRITE |
711 : : G_PARAM_CONSTRUCT_ONLY |
712 : : G_PARAM_EXPLICIT_NOTIFY |
713 : : G_PARAM_STATIC_STRINGS));
714 : :
715 : 3 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
716 : 3 : }
717 : :
718 : : static void
719 : 15 : valent_message_thread_init (ValentMessageThread *self)
720 : : {
721 : 15 : self->items = g_sequence_new (g_object_unref);
722 : 15 : }
723 : :
|