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-message-row"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <adwaita.h>
9 : : #include <gtk/gtk.h>
10 : : #include <pango/pango.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-date-label.h"
14 : : #include "valent-message-row.h"
15 : : #include "valent-message.h"
16 : : #include "valent-sms-utils.h"
17 : :
18 : :
19 : : struct _ValentMessageRow
20 : : {
21 : : GtkListBoxRow parent_instance;
22 : :
23 : : ValentMessage *message;
24 : : EContact *contact;
25 : :
26 : : GtkWidget *grid;
27 : : GtkWidget *avatar;
28 : : GtkWidget *name_label;
29 : : GtkWidget *date_label;
30 : : GtkWidget *body_label;
31 : : };
32 : :
33 [ + + + - ]: 25 : G_DEFINE_FINAL_TYPE (ValentMessageRow, valent_message_row, GTK_TYPE_LIST_BOX_ROW)
34 : :
35 : :
36 : : enum {
37 : : PROP_0,
38 : : PROP_CONTACT,
39 : : PROP_MESSAGE,
40 : : PROP_THREAD_ID,
41 : : N_PROPERTIES
42 : : };
43 : :
44 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
45 : :
46 : :
47 : : /*
48 : : * GObject
49 : : */
50 : : static void
51 : 3 : valent_message_row_finalize (GObject *object)
52 : : {
53 : 3 : ValentMessageRow *self = VALENT_MESSAGE_ROW (object);
54 : :
55 [ + - ]: 3 : g_clear_object (&self->contact);
56 [ + - ]: 3 : g_clear_object (&self->message);
57 : :
58 : 3 : G_OBJECT_CLASS (valent_message_row_parent_class)->finalize (object);
59 : 3 : }
60 : :
61 : : static void
62 : 3 : valent_message_row_get_property (GObject *object,
63 : : guint prop_id,
64 : : GValue *value,
65 : : GParamSpec *pspec)
66 : : {
67 : 3 : ValentMessageRow *self = VALENT_MESSAGE_ROW (object);
68 : :
69 [ + + + - ]: 3 : switch (prop_id)
70 : : {
71 : 1 : case PROP_CONTACT:
72 : 1 : g_value_set_object (value, self->contact);
73 : 1 : break;
74 : :
75 : 1 : case PROP_MESSAGE:
76 : 1 : g_value_set_object (value, self->message);
77 : 1 : break;
78 : :
79 : 1 : case PROP_THREAD_ID:
80 : 1 : g_value_set_int64 (value, valent_message_row_get_thread_id (self));
81 : 1 : break;
82 : :
83 : 0 : default:
84 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
85 : : }
86 : 3 : }
87 : :
88 : : static void
89 : 6 : valent_message_row_set_property (GObject *object,
90 : : guint prop_id,
91 : : const GValue *value,
92 : : GParamSpec *pspec)
93 : : {
94 : 6 : ValentMessageRow *self = VALENT_MESSAGE_ROW (object);
95 : :
96 [ + + - ]: 6 : switch (prop_id)
97 : : {
98 : 3 : case PROP_CONTACT:
99 : 3 : valent_message_row_set_contact (self, g_value_get_object (value));
100 : 3 : break;
101 : :
102 : 3 : case PROP_MESSAGE:
103 : 3 : valent_message_row_set_message (self, g_value_get_object (value));
104 : 3 : break;
105 : :
106 : 0 : default:
107 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
108 : : }
109 : 6 : }
110 : :
111 : : static void
112 : 2 : valent_message_row_class_init (ValentMessageRowClass *klass)
113 : : {
114 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
115 : :
116 : 2 : object_class->finalize = valent_message_row_finalize;
117 : 2 : object_class->get_property = valent_message_row_get_property;
118 : 2 : object_class->set_property = valent_message_row_set_property;
119 : :
120 : : /**
121 : : * ValentMessageRow:contact
122 : : *
123 : : * The `EContact` that sent this message.
124 : : */
125 : 4 : properties [PROP_CONTACT] =
126 : 2 : g_param_spec_object ("contact", NULL, NULL,
127 : : E_TYPE_CONTACT,
128 : : (G_PARAM_READWRITE |
129 : : G_PARAM_CONSTRUCT |
130 : : G_PARAM_EXPLICIT_NOTIFY |
131 : : G_PARAM_STATIC_STRINGS));
132 : :
133 : : /**
134 : : * ValentMessageRow:message
135 : : *
136 : : * The message this row displays.
137 : : */
138 : 4 : properties [PROP_MESSAGE] =
139 : 2 : g_param_spec_object ("message", NULL, NULL,
140 : : VALENT_TYPE_MESSAGE,
141 : : (G_PARAM_READWRITE |
142 : : G_PARAM_CONSTRUCT |
143 : : G_PARAM_EXPLICIT_NOTIFY |
144 : : G_PARAM_STATIC_STRINGS));
145 : :
146 : : /**
147 : : * ValentMessageRow:thread-id
148 : : *
149 : : * The thread id this message belongs to.
150 : : */
151 : 4 : properties [PROP_THREAD_ID] =
152 : 2 : g_param_spec_int64 ("thread-id", NULL, NULL,
153 : : G_MININT64, G_MAXINT64,
154 : : 0,
155 : : (G_PARAM_READABLE |
156 : : G_PARAM_EXPLICIT_NOTIFY |
157 : : G_PARAM_STATIC_STRINGS));
158 : :
159 : 2 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
160 : 2 : }
161 : :
162 : : static void
163 : 3 : valent_message_row_init (ValentMessageRow *self)
164 : : {
165 : 3 : gtk_widget_add_css_class (GTK_WIDGET (self), "valent-message-row");
166 : :
167 : 3 : self->grid = g_object_new (GTK_TYPE_GRID,
168 : : "column-spacing", 8,
169 : : "margin-start", 8,
170 : : "margin-end", 8,
171 : : "margin-top", 6,
172 : : "margin-bottom", 6,
173 : : NULL);
174 : 3 : gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (self), self->grid);
175 : :
176 : 3 : self->avatar = g_object_new (ADW_TYPE_AVATAR,
177 : : "size", 32,
178 : : "halign", GTK_ALIGN_START,
179 : : "valign", GTK_ALIGN_CENTER,
180 : : "vexpand", TRUE,
181 : : NULL);
182 : 3 : gtk_grid_attach (GTK_GRID (self->grid), self->avatar, 0, 0, 1, 2);
183 : :
184 : 3 : self->name_label = g_object_new (GTK_TYPE_LABEL,
185 : : "ellipsize", PANGO_ELLIPSIZE_END,
186 : : "halign", GTK_ALIGN_START,
187 : : "hexpand", TRUE,
188 : : "valign", GTK_ALIGN_START,
189 : : "vexpand", TRUE,
190 : : "use-markup", TRUE,
191 : : "xalign", 0.0,
192 : : NULL);
193 : 3 : gtk_grid_attach (GTK_GRID (self->grid), self->name_label, 1, 0, 1, 1);
194 : :
195 : 3 : self->date_label = g_object_new (VALENT_TYPE_DATE_LABEL,
196 : : "halign", GTK_ALIGN_END,
197 : : NULL);
198 : 3 : gtk_widget_add_css_class (self->date_label, "dim-label");
199 : 3 : gtk_grid_attach (GTK_GRID (self->grid), self->date_label, 2, 0, 1, 1);
200 : :
201 : 3 : self->body_label = g_object_new (GTK_TYPE_LABEL,
202 : : "ellipsize", PANGO_ELLIPSIZE_END,
203 : : "halign", GTK_ALIGN_START,
204 : : "hexpand", TRUE,
205 : : "valign", GTK_ALIGN_END,
206 : : "vexpand", TRUE,
207 : : "single-line-mode", TRUE,
208 : : "use-markup", TRUE,
209 : : "xalign", 0.0,
210 : : NULL);
211 : 3 : gtk_grid_attach (GTK_GRID (self->grid), self->body_label, 1, 1, 2, 1);
212 : 3 : }
213 : :
214 : : /**
215 : : * valent_message_row_new:
216 : : * @message: (nullable): a `ValentMessage`
217 : : * @contact: (nullable): a `EContact`
218 : : *
219 : : * Create a new message row for @contact and @message.
220 : : *
221 : : * Returns: a `ValentMessageRow`
222 : : */
223 : : GtkWidget *
224 : 1 : valent_message_row_new (ValentMessage *message,
225 : : EContact *contact)
226 : : {
227 : 1 : return g_object_new (VALENT_TYPE_MESSAGE_ROW,
228 : : "contact", contact,
229 : : "message", message,
230 : : NULL);
231 : : }
232 : :
233 : : /**
234 : : * valent_message_row_get_contact:
235 : : * @row: a `ValentMessageRow`
236 : : *
237 : : * Get the contact.
238 : : *
239 : : * Returns: (transfer none) (nullable): a `ValentContact`
240 : : */
241 : : EContact *
242 : 1 : valent_message_row_get_contact (ValentMessageRow *row)
243 : : {
244 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_MESSAGE_ROW (row), NULL);
245 : :
246 : 1 : return row->contact;
247 : : }
248 : :
249 : : /**
250 : : * valent_message_row_set_contact:
251 : : * @row: a `ValentMessageRow`
252 : : * @contact: a `ValentContact`
253 : : *
254 : : * Set or update the contact.
255 : : */
256 : : void
257 : 5 : valent_message_row_set_contact (ValentMessageRow *row,
258 : : EContact *contact)
259 : : {
260 [ + - ]: 5 : g_return_if_fail (VALENT_IS_MESSAGE_ROW (row));
261 [ + + + - : 5 : g_return_if_fail (contact == NULL || E_IS_CONTACT (contact));
- + - - ]
262 : :
263 [ + + ]: 5 : if (!g_set_object (&row->contact, contact))
264 : : return;
265 : :
266 [ + - ]: 3 : if (row->contact != NULL)
267 : 3 : valent_sms_avatar_from_contact (ADW_AVATAR (row->avatar), contact);
268 : :
269 : 3 : valent_message_row_update (row);
270 : 3 : g_object_notify_by_pspec (G_OBJECT (row), properties [PROP_CONTACT]);
271 : : }
272 : :
273 : : /**
274 : : * valent_message_row_get_thread_id:
275 : : * @row: a `ValentMessageRow`
276 : : *
277 : : * Get the thread_id of the message.
278 : : *
279 : : * Returns: a thread id
280 : : */
281 : : int64_t
282 : 2 : valent_message_row_get_thread_id (ValentMessageRow *row)
283 : : {
284 [ + - ]: 2 : g_return_val_if_fail (VALENT_IS_MESSAGE_ROW (row), 0);
285 : :
286 [ - + ]: 2 : if G_UNLIKELY (row->message == NULL)
287 : : return 0;
288 : :
289 : 2 : return valent_message_get_thread_id (row->message);
290 : : }
291 : :
292 : : /**
293 : : * valent_message_row_get_message:
294 : : * @row: a `ValentMessageRow`
295 : : *
296 : : * Get the message.
297 : : *
298 : : * Returns: (transfer none): a `ValentMessage`
299 : : */
300 : : ValentMessage *
301 : 1 : valent_message_row_get_message (ValentMessageRow *row)
302 : : {
303 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_MESSAGE_ROW (row), NULL);
304 : :
305 : 1 : return row->message;
306 : : }
307 : :
308 : : /**
309 : : * valent_message_row_set_message:
310 : : * @row: a `ValentMessageRow`
311 : : * @message: a `ValentMessage`
312 : : *
313 : : * Set or update the message.
314 : : */
315 : : void
316 : 3 : valent_message_row_set_message (ValentMessageRow *row,
317 : : ValentMessage *message)
318 : : {
319 [ + - ]: 3 : g_return_if_fail (VALENT_IS_MESSAGE_ROW (row));
320 [ + - - + ]: 3 : g_return_if_fail (message == NULL || VALENT_IS_MESSAGE (message));
321 : :
322 [ + - ]: 3 : if (row->message == message)
323 : : return;
324 : :
325 [ - + ]: 3 : if (row->message != NULL)
326 : : {
327 : 0 : g_signal_handlers_disconnect_by_data (row->message, row);
328 [ # # ]: 0 : g_clear_object (&row->message);
329 : : }
330 : :
331 [ + - ]: 3 : if (message != NULL)
332 : : {
333 : 3 : row->message = g_object_ref (message);
334 : 3 : g_signal_connect_swapped (row->message,
335 : : "notify",
336 : : G_CALLBACK (valent_message_row_update),
337 : : row);
338 : :
339 : 3 : valent_message_row_update (row);
340 : 3 : g_object_notify_by_pspec (G_OBJECT (row), properties [PROP_MESSAGE]);
341 : : }
342 : : }
343 : :
344 : : /**
345 : : * valent_message_row_update:
346 : : * @row: a `ValentMessageRow`
347 : : *
348 : : * Update the conversation row with data from `ValentMessageRow`:contact
349 : : * and `ValentMessageRow`:message properties.
350 : : */
351 : : void
352 : 6 : valent_message_row_update (ValentMessageRow *row)
353 : : {
354 : 6 : gboolean read;
355 : 6 : const char *body;
356 : 6 : const char *name;
357 : 6 : int64_t date;
358 : 6 : g_autofree char *body_label = NULL;
359 : 6 : g_autofree char *name_label = NULL;
360 : :
361 [ + - ]: 6 : g_return_if_fail (VALENT_IS_MESSAGE_ROW (row));
362 : :
363 [ + + ]: 6 : if (row->message == NULL)
364 : : return;
365 : :
366 : : /* Message Sender/Name */
367 [ + + + - : 5 : if (E_IS_CONTACT (row->contact))
- + - - ]
368 : 3 : name = e_contact_get_const (row->contact, E_CONTACT_FULL_NAME);
369 : : else
370 : 2 : name = valent_message_get_sender (row->message);
371 : :
372 : : /* Message Read/Unread */
373 [ + + ]: 5 : if ((read = valent_message_get_read (row->message)))
374 [ - + ]: 1 : name_label = g_strdup (name);
375 : : else
376 : 4 : name_label = g_strdup_printf ("<b>%s</b>", name);
377 : :
378 : 5 : gtk_label_set_label (GTK_LABEL (row->name_label), name_label);
379 : :
380 : : /* Message Body */
381 [ + - ]: 5 : if ((body = valent_message_get_text (row->message)) != NULL)
382 : : {
383 : 5 : g_autofree char *text = NULL;
384 : :
385 : 5 : text = g_markup_escape_text (body, -1);
386 : :
387 [ + + ]: 5 : if (valent_message_get_box (row->message) == VALENT_MESSAGE_BOX_SENT)
388 : 4 : body_label = g_strdup_printf ("<small>You: %s</small>", text);
389 [ + - ]: 1 : else if (read)
390 : 1 : body_label = g_strdup_printf ("<small>%s</small>", text);
391 : : else
392 : 0 : body_label = g_strdup_printf ("<b><small>%s</small></b>", text);
393 : : }
394 : :
395 : 5 : gtk_label_set_label (GTK_LABEL (row->body_label), body_label);
396 : :
397 : : /* Message Date */
398 : 5 : date = valent_message_get_date (row->message);
399 : 5 : valent_date_label_set_date (VALENT_DATE_LABEL (row->date_label), date);
400 : : }
401 : :
|