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