LCOV - code coverage report
Current view: top level - src/plugins/sms - valent-date-label.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 79.8 % 129 103
Test Date: 2024-03-22 06:22:29 Functions: 94.1 % 17 16
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 53.1 % 64 34

             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-date-label"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <glib/gi18n.h>
       9                 :             : #include <gtk/gtk.h>
      10                 :             : #include <valent.h>
      11                 :             : 
      12                 :             : #include "valent-date-label.h"
      13                 :             : 
      14                 :             : 
      15                 :             : struct _ValentDateLabel
      16                 :             : {
      17                 :             :   GtkWidget     parent_instance;
      18                 :             : 
      19                 :             :   GtkWidget    *label;
      20                 :             :   int64_t       date;
      21                 :             :   unsigned int  mode;
      22                 :             : };
      23                 :             : 
      24   [ +  +  +  - ]:          24 : G_DEFINE_FINAL_TYPE (ValentDateLabel, valent_date_label, GTK_TYPE_WIDGET)
      25                 :             : 
      26                 :             : enum {
      27                 :             :   PROP_0,
      28                 :             :   PROP_DATE,
      29                 :             :   PROP_MODE,
      30                 :             :   N_PROPERTIES
      31                 :             : };
      32                 :             : 
      33                 :             : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
      34                 :             : 
      35                 :             : static GPtrArray *label_cache = NULL;
      36                 :             : static unsigned int label_source = 0;
      37                 :             : 
      38                 :             : 
      39                 :             : /**
      40                 :             :  * valent_date_label_string:
      41                 :             :  * @timestamp: a UNIX epoch timestamp (ms)
      42                 :             :  *
      43                 :             :  * Create a user friendly date-time string for @timestamp, in a relative format.
      44                 :             :  *
      45                 :             :  * Examples:
      46                 :             :  *     - "Just now"
      47                 :             :  *     - "15 minutes"
      48                 :             :  *     - "11:45 PM"
      49                 :             :  *     - "Yesterday · 11:45 PM"
      50                 :             :  *     - "Tuesday"
      51                 :             :  *     - "February 29"
      52                 :             :  *
      53                 :             :  * Returns: (transfer full): a new string
      54                 :             :  */
      55                 :             : static char *
      56                 :           1 : valent_date_label_string (int64_t timestamp)
      57                 :             : {
      58                 :           2 :   g_autoptr (GDateTime) dt = NULL;
      59         [ +  - ]:           1 :   g_autoptr (GDateTime) now = NULL;
      60                 :           1 :   GTimeSpan diff;
      61                 :             : 
      62                 :           1 :   dt = g_date_time_new_from_unix_local (timestamp / 1000);
      63                 :           1 :   now = g_date_time_new_now_local ();
      64                 :           1 :   diff = g_date_time_difference (now, dt);
      65                 :             : 
      66                 :             :   /* TRANSLATORS: Less than a minute ago */
      67         [ -  + ]:           1 :   if (diff < G_TIME_SPAN_MINUTE)
      68         [ #  # ]:           0 :       return g_strdup (_("Just now"));
      69                 :             : 
      70                 :             :   /* TRANSLATORS: Time duration in minutes (eg. 15 minutes) */
      71         [ -  + ]:           1 :   if (diff < G_TIME_SPAN_HOUR)
      72                 :             :     {
      73                 :           0 :       unsigned int n_minutes;
      74                 :             : 
      75                 :           0 :       n_minutes = (diff / G_TIME_SPAN_MINUTE);
      76                 :           0 :       return g_strdup_printf (ngettext("%d minute", "%d minutes", n_minutes),
      77                 :             :                               n_minutes);
      78                 :             :     }
      79                 :             : 
      80                 :             :   /* TRANSLATORS: Yesterday, but less than 24 hours (eg. Yesterday · 11:45 PM) */
      81         [ -  + ]:           1 :   if (diff < G_TIME_SPAN_DAY)
      82                 :             :     {
      83                 :           0 :       g_autofree char *time_str = NULL;
      84                 :           0 :       int today, day;
      85                 :             : 
      86                 :           0 :       today = g_date_time_get_day_of_month(now);
      87                 :           0 :       day = g_date_time_get_day_of_month(dt);
      88                 :           0 :       time_str = g_date_time_format(dt, "%l:%M %p");
      89                 :             : 
      90         [ #  # ]:           0 :       if (today == day)
      91                 :             :         return g_steal_pointer (&time_str);
      92                 :             :       else
      93                 :           0 :         return g_strdup_printf (_("Yesterday · %s"), time_str);
      94                 :             :     }
      95                 :             : 
      96                 :             :   /* Less than a week ago (eg. Tuesday) */
      97         [ -  + ]:           1 :   if (diff < G_TIME_SPAN_DAY * 7)
      98                 :           0 :     return g_date_time_format(dt, "%A");
      99                 :             : 
     100                 :             :   /* More than a week ago (eg. February 29) */
     101                 :           1 :   return g_date_time_format(dt, "%B %e");
     102                 :             : }
     103                 :             : 
     104                 :             : /**
     105                 :             :  * valent_date_label_string_short:
     106                 :             :  * @timestamp: a UNIX epoch timestamp (ms)
     107                 :             :  *
     108                 :             :  * Create a user friendly date-time string for @timestamp, in a relative format.
     109                 :             :  * This is like valent_date_label_string() but abbreviated.
     110                 :             :  *
     111                 :             :  * Examples:
     112                 :             :  *     - "Just now"
     113                 :             :  *     - "15 mins"
     114                 :             :  *     - "11:45 PM"
     115                 :             :  *     - "Tue"
     116                 :             :  *     - "Feb 29"
     117                 :             :  *
     118                 :             :  * Returns: (transfer full): a new string
     119                 :             :  */
     120                 :             : static char *
     121                 :           4 : valent_date_label_string_short (int64_t timestamp)
     122                 :             : {
     123                 :           8 :   g_autoptr (GDateTime) dt = NULL;
     124         [ +  - ]:           4 :   g_autoptr (GDateTime) now = NULL;
     125                 :           4 :   GTimeSpan diff;
     126                 :             : 
     127                 :           4 :   dt = g_date_time_new_from_unix_local (timestamp / 1000);
     128                 :           4 :   now = g_date_time_new_now_local ();
     129                 :           4 :   diff = g_date_time_difference (now, dt);
     130                 :             : 
     131                 :             :   /* TRANSLATORS: Less than a minute ago */
     132         [ -  + ]:           4 :   if (diff < G_TIME_SPAN_MINUTE)
     133         [ #  # ]:           0 :       return g_strdup (_("Just now"));
     134                 :             : 
     135                 :             :   /* TRANSLATORS: Time duration in minutes, abbreviated (eg. 15 mins) */
     136         [ -  + ]:           4 :   if (diff < G_TIME_SPAN_HOUR)
     137                 :             :     {
     138                 :           0 :       unsigned int n_minutes;
     139                 :             : 
     140                 :           0 :       n_minutes = (diff / G_TIME_SPAN_MINUTE);
     141                 :           0 :       return g_strdup_printf (ngettext ("%d min", "%d mins", n_minutes),
     142                 :             :                               n_minutes);
     143                 :             :     }
     144                 :             : 
     145                 :             : 
     146                 :             :   /* Less than a day ago (eg. 11:45 PM) */
     147         [ -  + ]:           4 :   if (diff < G_TIME_SPAN_DAY)
     148                 :           0 :     return g_date_time_format (dt, "%l:%M %p");
     149                 :             : 
     150                 :             :   /* Less than a week ago (eg. Tue) */
     151         [ -  + ]:           4 :   if (diff < G_TIME_SPAN_DAY * 7)
     152                 :           0 :     return g_date_time_format (dt, "%a");
     153                 :             : 
     154                 :             :   /* More than a week ago (eg. Feb 29) */
     155                 :           4 :   return g_date_time_format (dt, "%b %e");
     156                 :             : }
     157                 :             : 
     158                 :             : static gboolean
     159                 :           0 : valent_date_label_update_func (gpointer user_data)
     160                 :             : {
     161         [ #  # ]:           0 :   for (unsigned int i = 0; i < label_cache->len; i++)
     162                 :           0 :     valent_date_label_update (g_ptr_array_index (label_cache, i));
     163                 :             : 
     164                 :           0 :   return G_SOURCE_CONTINUE;
     165                 :             : }
     166                 :             : 
     167                 :             : /*
     168                 :             :  * GObject
     169                 :             :  */
     170                 :             : static void
     171                 :           4 : valent_date_label_finalize (GObject *object)
     172                 :             : {
     173                 :           4 :   ValentDateLabel *self = VALENT_DATE_LABEL (object);
     174                 :             : 
     175                 :             :   /* Remove from update list */
     176                 :           4 :   g_ptr_array_remove (label_cache, self);
     177                 :             : 
     178         [ +  + ]:           4 :   if (label_cache->len == 0)
     179                 :             :     {
     180         [ +  - ]:           3 :       g_clear_handle_id (&label_source, g_source_remove);
     181         [ +  - ]:           3 :       g_clear_pointer (&label_cache, g_ptr_array_unref);
     182                 :             :     }
     183                 :             : 
     184         [ +  - ]:           4 :   g_clear_pointer (&self->label, gtk_widget_unparent);
     185                 :             : 
     186                 :           4 :   G_OBJECT_CLASS (valent_date_label_parent_class)->finalize (object);
     187                 :           4 : }
     188                 :             : 
     189                 :             : static void
     190                 :           2 : valent_date_label_get_property (GObject    *object,
     191                 :             :                                 guint       prop_id,
     192                 :             :                                 GValue     *value,
     193                 :             :                                 GParamSpec *pspec)
     194                 :             : {
     195                 :           2 :   ValentDateLabel *self = VALENT_DATE_LABEL (object);
     196                 :             : 
     197      [ +  +  - ]:           2 :   switch (prop_id)
     198                 :             :     {
     199                 :           1 :     case PROP_DATE:
     200                 :           1 :       g_value_set_int64 (value, valent_date_label_get_date (self));
     201                 :           1 :       break;
     202                 :             : 
     203                 :           1 :     case PROP_MODE:
     204                 :           1 :       g_value_set_uint (value, valent_date_label_get_mode (self));
     205                 :           1 :       break;
     206                 :             : 
     207                 :           0 :     default:
     208                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     209                 :             :     }
     210                 :           2 : }
     211                 :             : 
     212                 :             : static void
     213                 :           2 : valent_date_label_set_property (GObject      *object,
     214                 :             :                                 guint         prop_id,
     215                 :             :                                 const GValue *value,
     216                 :             :                                 GParamSpec   *pspec)
     217                 :             : {
     218                 :           2 :   ValentDateLabel *self = VALENT_DATE_LABEL (object);
     219                 :             : 
     220      [ +  +  - ]:           2 :   switch (prop_id)
     221                 :             :     {
     222                 :           1 :     case PROP_DATE:
     223                 :           1 :       valent_date_label_set_date (self, g_value_get_int64 (value));
     224                 :           1 :       break;
     225                 :             : 
     226                 :           1 :     case PROP_MODE:
     227                 :           1 :       valent_date_label_set_mode (self, g_value_get_uint (value));
     228                 :           1 :       break;
     229                 :             : 
     230                 :           0 :     default:
     231                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     232                 :             :     }
     233                 :           2 : }
     234                 :             : 
     235                 :             : static void
     236                 :           3 : valent_date_label_class_init (ValentDateLabelClass *klass)
     237                 :             : {
     238                 :           3 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     239                 :           3 :   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
     240                 :             : 
     241                 :           3 :   object_class->finalize = valent_date_label_finalize;
     242                 :           3 :   object_class->get_property = valent_date_label_get_property;
     243                 :           3 :   object_class->set_property = valent_date_label_set_property;
     244                 :             : 
     245                 :           3 :   gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
     246                 :           3 :   gtk_widget_class_set_css_name (widget_class, "date-label");
     247                 :             : 
     248                 :             :   /**
     249                 :             :    * ValentDateLabel:date
     250                 :             :    *
     251                 :             :    * The timestamp this label represents.
     252                 :             :    */
     253                 :           6 :   properties [PROP_DATE] =
     254                 :           3 :     g_param_spec_int64 ("date", NULL, NULL,
     255                 :             :                         0, G_MAXINT64,
     256                 :             :                         0,
     257                 :             :                         (G_PARAM_READWRITE |
     258                 :             :                          G_PARAM_EXPLICIT_NOTIFY |
     259                 :             :                          G_PARAM_STATIC_STRINGS));
     260                 :             : 
     261                 :             :   /**
     262                 :             :    * ValentDateLabel:mode
     263                 :             :    *
     264                 :             :    * The brevity of the label.
     265                 :             :    */
     266                 :           6 :   properties [PROP_MODE] =
     267                 :           3 :     g_param_spec_uint ("mode", NULL, NULL,
     268                 :             :                        0, G_MAXUINT32,
     269                 :             :                        0,
     270                 :             :                        (G_PARAM_READWRITE |
     271                 :             :                         G_PARAM_EXPLICIT_NOTIFY |
     272                 :             :                         G_PARAM_STATIC_STRINGS));
     273                 :             : 
     274                 :           3 :   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
     275                 :           3 : }
     276                 :             : 
     277                 :             : static void
     278                 :           4 : valent_date_label_init (ValentDateLabel *self)
     279                 :             : {
     280                 :           4 :   self->label = gtk_label_new (NULL);
     281                 :           4 :   gtk_widget_insert_after (self->label, GTK_WIDGET (self), NULL);
     282                 :             : 
     283                 :             :   /* Prepare the update list */
     284         [ +  + ]:           4 :   if (label_cache == NULL)
     285                 :             :     {
     286                 :           3 :       label_cache = g_ptr_array_new ();
     287                 :           3 :       label_source = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT_IDLE,
     288                 :             :                                                  60,
     289                 :             :                                                  valent_date_label_update_func,
     290                 :             :                                                  NULL,
     291                 :             :                                                  NULL);
     292                 :             :     }
     293                 :             : 
     294                 :           4 :   g_ptr_array_add (label_cache, self);
     295                 :           4 : }
     296                 :             : 
     297                 :             : /**
     298                 :             :  * valent_date_label_new:
     299                 :             :  * @date: a UNIX epoch timestamp
     300                 :             :  *
     301                 :             :  * Create a new `ValentDateLabel` for @timestamp.
     302                 :             :  *
     303                 :             :  * Returns: (transfer full): a `GtkWidget`
     304                 :             :  */
     305                 :             : GtkWidget *
     306                 :           1 : valent_date_label_new (int64_t date)
     307                 :             : {
     308                 :           1 :   return g_object_new (VALENT_TYPE_DATE_LABEL,
     309                 :             :                        "date", date,
     310                 :             :                        NULL);
     311                 :             : }
     312                 :             : 
     313                 :             : /**
     314                 :             :  * valent_date_label_get_date:
     315                 :             :  * @label: a `ValentDateLabel`
     316                 :             :  *
     317                 :             :  * Get the UNIX epoch timestamp (ms) for @label.
     318                 :             :  *
     319                 :             :  * Returns: the timestamp
     320                 :             :  */
     321                 :             : int64_t
     322                 :           1 : valent_date_label_get_date (ValentDateLabel *label)
     323                 :             : {
     324         [ +  - ]:           1 :   g_return_val_if_fail (VALENT_IS_DATE_LABEL (label), 0);
     325                 :             : 
     326                 :           1 :   return label->date;
     327                 :             : }
     328                 :             : 
     329                 :             : /**
     330                 :             :  * valent_date_label_set_date:
     331                 :             :  * @label: a `ValentDateLabel`
     332                 :             :  * @date: a UNIX epoch timestamp
     333                 :             :  *
     334                 :             :  * Set the timestamp for @label to @date.
     335                 :             :  */
     336                 :             : void
     337                 :           6 : valent_date_label_set_date (ValentDateLabel *label,
     338                 :             :                             int64_t          date)
     339                 :             : {
     340         [ +  - ]:           6 :   g_return_if_fail (VALENT_IS_DATE_LABEL (label));
     341                 :             : 
     342         [ +  + ]:           6 :   if (label->date == date)
     343                 :             :     return;
     344                 :             : 
     345                 :           4 :   label->date = date;
     346                 :           4 :   valent_date_label_update (label);
     347                 :           4 :   g_object_notify_by_pspec (G_OBJECT (label), properties [PROP_DATE]);
     348                 :             : }
     349                 :             : 
     350                 :             : /**
     351                 :             :  * valent_date_label_get_mode:
     352                 :             :  * @label: a `ValentDateLabel`
     353                 :             :  *
     354                 :             :  * Get the display mode @label.
     355                 :             :  *
     356                 :             :  * Returns: the display mode
     357                 :             :  */
     358                 :             : unsigned int
     359                 :           1 : valent_date_label_get_mode (ValentDateLabel *label)
     360                 :             : {
     361         [ +  - ]:           1 :   g_return_val_if_fail (VALENT_IS_DATE_LABEL (label), 0);
     362                 :             : 
     363                 :           1 :   return label->mode;
     364                 :             : }
     365                 :             : 
     366                 :             : /**
     367                 :             :  * valent_date_label_set_mode:
     368                 :             :  * @label: a `ValentDateLabel`
     369                 :             :  * @mode: a mode
     370                 :             :  *
     371                 :             :  * Set the mode of @label to @mode. Currently the options are `0` and `1`.
     372                 :             :  */
     373                 :             : void
     374                 :           1 : valent_date_label_set_mode (ValentDateLabel *label,
     375                 :             :                             unsigned int     mode)
     376                 :             : {
     377         [ +  - ]:           1 :   g_return_if_fail (VALENT_IS_DATE_LABEL (label));
     378                 :             : 
     379         [ +  - ]:           1 :   if (label->mode == mode)
     380                 :             :     return;
     381                 :             : 
     382                 :           1 :   label->mode = mode;
     383                 :           1 :   valent_date_label_update (label);
     384                 :           1 :   g_object_notify_by_pspec (G_OBJECT (label), properties [PROP_MODE]);
     385                 :             : }
     386                 :             : 
     387                 :             : /**
     388                 :             :  * valent_date_label_update:
     389                 :             :  * @label: a `ValentDateLabel`
     390                 :             :  *
     391                 :             :  * Update the displayed text of @label.
     392                 :             :  */
     393                 :             : void
     394                 :           5 : valent_date_label_update (ValentDateLabel *label)
     395                 :             : {
     396                 :           5 :   g_autofree char *text = NULL;
     397                 :             : 
     398         [ +  - ]:           5 :   g_return_if_fail (VALENT_IS_DATE_LABEL (label));
     399                 :             : 
     400         [ +  + ]:           5 :   if (label->mode == 0)
     401                 :           4 :     text = valent_date_label_string_short (label->date);
     402                 :             :   else
     403                 :           1 :     text = valent_date_label_string (label->date);
     404                 :             : 
     405                 :           5 :   gtk_label_set_label (GTK_LABEL (label->label), text);
     406                 :             : }
     407                 :             : 
        

Generated by: LCOV version 2.0-1