anastasis-gtk

Demonstrator GUI for Anastasis
Log | Files | Refs | README | LICENSE

anastasis-gtk_helper.c (11408B)


      1 /*
      2      This file is part of anastasis-gtk.
      3      Copyright (C) 2020 Anastasis SARL
      4 
      5      Anastasis is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      Anastasis is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with Anastasis; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 
     21 /**
     22  * @file src/anastasis/anastasis-gtk_helper.c
     23  * @brief Helper functions of anastasis-gtk
     24  * @author Christian Grothoff
     25  * @author Dennis Neufeld
     26  */
     27 #define _GNU_SOURCE
     28 #include <stdio.h>
     29 #include "anastasis_gtk_util.h"
     30 #include <gnunet/gnunet_util_lib.h>
     31 #include "anastasis-gtk_helper.h"
     32 #include <jansson.h>
     33 #include <qrencode.h>
     34 #include <gdk-pixbuf/gdk-pixbuf.h>
     35 
     36 
     37 /**
     38  * true if we are currently showing an error message.
     39  */
     40 bool AG_have_error;
     41 
     42 
     43 void
     44 AG_thaw ()
     45 {
     46   AG_error_clear ();
     47   AG_sensitive ("anastasis_gtk_main_window");
     48   GNUNET_assert (NULL == AG_ra);
     49 }
     50 
     51 
     52 void
     53 AG_freeze ()
     54 {
     55   AG_insensitive ("anastasis_gtk_main_window");
     56   AG_stop_long_action ();
     57   GNUNET_assert (NULL == AG_ra);
     58 }
     59 
     60 
     61 void
     62 AG_sensitive (const char *name)
     63 {
     64   GtkWidget *w;
     65 
     66   w = GTK_WIDGET (GCG_get_main_window_object (name));
     67   if (NULL == w)
     68   {
     69     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     70                 "Widget `%s' not found, cannot make it sensitive!\n",
     71                 name);
     72     return;
     73   }
     74   gtk_widget_set_sensitive (w,
     75                             true);
     76 }
     77 
     78 
     79 void
     80 AG_focus (const char *name)
     81 {
     82   GtkWidget *w;
     83 
     84   w = GTK_WIDGET (GCG_get_main_window_object (name));
     85   if (NULL == w)
     86   {
     87     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     88                 "Widget `%s' not found, cannot focus on it!\n",
     89                 name);
     90     return;
     91   }
     92   gtk_widget_grab_focus (w);
     93 }
     94 
     95 
     96 void
     97 AG_insensitive (const char *name)
     98 {
     99   GtkWidget *w;
    100 
    101   w = GTK_WIDGET (GCG_get_main_window_object (name));
    102   if (NULL == w)
    103   {
    104     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    105                 "Widget `%s' not found, cannot make it sensitive!\n",
    106                 name);
    107     return;
    108   }
    109   gtk_widget_set_sensitive (w,
    110                             false);
    111 }
    112 
    113 
    114 void
    115 AG_hide (const char *name)
    116 {
    117   GtkWidget *w;
    118 
    119   w = GTK_WIDGET (GCG_get_main_window_object (name));
    120   if (NULL == w)
    121   {
    122     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    123                 "Widget `%s' not found, cannot hide it!\n",
    124                 name);
    125     return;
    126   }
    127   gtk_widget_hide (w);
    128 }
    129 
    130 
    131 void
    132 AG_show (const char *name)
    133 {
    134   GtkWidget *w;
    135 
    136   w = GTK_WIDGET (GCG_get_main_window_object (name));
    137   if (NULL == w)
    138   {
    139     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    140                 "Widget `%s' not found, cannot show it!\n",
    141                 name);
    142     return;
    143   }
    144   gtk_widget_show (w);
    145 }
    146 
    147 
    148 void
    149 AG_insensitive_children (const char *name)
    150 {
    151   GList *children;
    152 
    153   children = gtk_container_get_children (GTK_CONTAINER (
    154                                            GCG_get_main_window_object (name)));
    155   for (GList *iter = children; iter != NULL; iter = g_list_next (iter))
    156     gtk_widget_set_sensitive (GTK_WIDGET (iter->data),
    157                               false);
    158   g_list_free (children);
    159 }
    160 
    161 
    162 void
    163 AG_hide_children (const char *name)
    164 {
    165   GList *children;
    166 
    167   children = gtk_container_get_children (GTK_CONTAINER (
    168                                            GCG_get_main_window_object (name)));
    169   for (GList *iter = children; iter != NULL; iter = g_list_next (iter))
    170     gtk_widget_hide (GTK_WIDGET (iter->data));
    171   g_list_free (children);
    172 }
    173 
    174 
    175 void
    176 AG_show_children (const char *name)
    177 {
    178   GList *children;
    179 
    180   children = gtk_container_get_children (GTK_CONTAINER (
    181                                            GCG_get_main_window_object (name)));
    182   for (GList *iter = children; iter != NULL; iter = g_list_next (iter))
    183     gtk_widget_show (GTK_WIDGET (iter->data));
    184   g_list_free (children);
    185 
    186 }
    187 
    188 
    189 void
    190 AG_hide_all_frames (void)
    191 {
    192   AG_hide ("anastasis_gtk_start_frame");
    193   AG_hide_children ("anastasis_gtk_super_vbox");
    194   if (AG_have_error)
    195     AG_show ("anastasis_gtk_error_label");
    196 }
    197 
    198 
    199 bool
    200 AG_check_state (json_t *state,
    201                 const char *expected_state)
    202 {
    203   const char *state_name = json_string_value (json_object_get (state,
    204                                                                "backup_state"));
    205   if (NULL == state_name)
    206     state_name = json_string_value (json_object_get (state,
    207                                                      "recovery_state"));
    208   if (NULL == state_name)
    209     return false;
    210   return (0 == strcasecmp (state_name,
    211                            expected_state));
    212 }
    213 
    214 
    215 /**
    216  * Get an object from the main window.
    217  *
    218  * @param name name of the object
    219  * @return NULL on error
    220  */
    221 GObject *
    222 GCG_get_main_window_object (const char *name)
    223 {
    224   if (NULL == AG_ml)
    225     return NULL;
    226   return ANASTASIS_GTK_main_loop_get_object (AG_ml,
    227                                           name);
    228 }
    229 
    230 
    231 /**
    232  * Make the 'next' button sensitive (and trigger it via 'Return').
    233  */
    234 void
    235 AG_enable_next (void)
    236 {
    237   GtkWidget *fwd;
    238 
    239   AG_show ("anastasis_gtk_main_window_forward_button");
    240   AG_sensitive ("anastasis_gtk_main_window_forward_button");
    241   fwd = GTK_WIDGET (GCG_get_main_window_object (
    242                       "anastasis_gtk_main_window_forward_button"));
    243   gtk_widget_set_can_default (fwd,
    244                               true);
    245   gtk_widget_grab_default (fwd);
    246 
    247 }
    248 
    249 
    250 void
    251 AG_error_clear ()
    252 {
    253   AG_have_error = false;
    254   AG_hide ("anastasis_gtk_error_label");
    255 }
    256 
    257 
    258 void
    259 AG_error (const char *format,
    260           ...)
    261 {
    262   va_list ap;
    263   char *msg;
    264   int ret;
    265   GtkLabel *l;
    266 
    267   va_start (ap, format);
    268   ret = vasprintf (&msg,
    269                    format,
    270                    ap);
    271   va_end (ap);
    272   if (-1 == ret)
    273   {
    274     GNUNET_break (0);
    275     return;
    276   }
    277   l = GTK_LABEL (GCG_get_main_window_object ("anastasis_gtk_error_label"));
    278   if (NULL == l)
    279   {
    280     GNUNET_break (0);
    281     return;
    282   }
    283   gtk_label_set_text (l,
    284                       msg);
    285   free (msg);
    286   AG_have_error = true;
    287   gtk_widget_show (GTK_WIDGET (l));
    288 }
    289 
    290 
    291 /**
    292  * Create a the QR code image from a given @a text.
    293  *
    294  * @param scale factor for scaling up the size of the image to create
    295  * @param text text to encode
    296  * @return NULL on error
    297  */
    298 static GdkPixbuf *
    299 create_qrcode (unsigned int scale,
    300                const char *text,
    301                size_t text_size)
    302 {
    303   QRinput *qri;
    304   QRcode *qrc;
    305   GdkPixbuf *pb;
    306   guchar *pixels;
    307   int n_channels;
    308   const char *dir;
    309   char *fn;
    310   unsigned int size;
    311 
    312   qri = QRinput_new2 (0, QR_ECLEVEL_M);
    313   if (NULL == qri)
    314   {
    315     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2");
    316     return NULL;
    317   }
    318   /* first try encoding as uppercase-only alpha-numerical
    319      QR code (much smaller encoding); if that fails, also
    320      try using binary encoding (in case nick contains
    321      special characters). */
    322   if ((0 != QRinput_append (qri,
    323                             QR_MODE_AN,
    324                             text_size,
    325                             (unsigned char *) text)) &&
    326       (0 != QRinput_append (qri,
    327                             QR_MODE_8,
    328                             text_size,
    329                             (unsigned char *) text)))
    330   {
    331     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    332                          "QRinput_append");
    333     return NULL;
    334   }
    335   qrc = QRcode_encodeInput (qri);
    336   if (NULL == qrc)
    337   {
    338     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    339                          "QRcode_encodeInput");
    340     QRinput_free (qri);
    341     return NULL;
    342   }
    343   /* We use a trick to create a pixbuf in a way that works for both Gtk2 and
    344      Gtk3 by loading a dummy file from disk; all other methods are not portable
    345      to both Gtk2 and Gtk3. */
    346   dir = ANASTASIS_GTK_get_data_dir (ANASTASIS_GTK_project_data ());
    347   GNUNET_asprintf (&fn,
    348                    "%s%s",
    349                    dir,
    350                    "qr_dummy.png");
    351   size = (qrc->width + 8) * scale;
    352   size += 8 - (size % 8);
    353   pb = gdk_pixbuf_new_from_file_at_size (fn,
    354                                          size,
    355                                          size,
    356                                          NULL);
    357   GNUNET_free (fn);
    358   if (NULL == pb)
    359   {
    360     QRcode_free (qrc);
    361     QRinput_free (qri);
    362     return NULL;
    363   }
    364   pixels = gdk_pixbuf_get_pixels (pb);
    365   n_channels = gdk_pixbuf_get_n_channels (pb);
    366   for (unsigned int x = 4 * scale; x < size - 4 * scale; x++)
    367     for (unsigned int y = 4 * scale; y < size - 4 * scale; y++)
    368     {
    369       unsigned int xx = x - 4 * scale;
    370       unsigned int yy = y - 4 * scale;
    371       unsigned int ss = size - 8 * scale;
    372       unsigned int off =
    373         (xx * qrc->width / ss) + (yy * qrc->width / ss) * qrc->width;
    374       for (int c = 0; c < n_channels; c++)
    375         pixels[(y * size + x) * n_channels + c] =
    376           (0 == (qrc->data[off] & 1)) ? 0xFF : 0;
    377     }
    378   QRcode_free (qrc);
    379   QRinput_free (qri);
    380   return pb;
    381 }
    382 
    383 
    384 GdkPixbuf *
    385 AG_setup_qrcode (GtkWidget *w,
    386                  const char *text,
    387                  size_t text_size)
    388 {
    389   GdkScreen *screen;
    390   GtkSettings *settings;
    391   gint dpi;
    392   int scale;
    393 
    394   if (NULL == w)
    395   {
    396     GNUNET_break (0);
    397     return NULL;
    398   }
    399   /* adjust scale to screen resolution */
    400   screen = gtk_widget_get_screen (w);
    401   settings = gtk_settings_get_for_screen (screen);
    402   g_object_get (G_OBJECT (settings),
    403                 "gtk-xft-dpi",
    404                 &dpi,
    405                 NULL);
    406   if (-1 == dpi)
    407     scale = 2;
    408   else if (dpi >= 122800)
    409     scale = 4;
    410   else if (dpi >= 98304)
    411     scale = 3;
    412   else
    413     scale = 2;
    414   return create_qrcode (scale,
    415                         text,
    416                         text_size);
    417 }
    418 
    419 
    420 char *
    421 AG_expand_name (const char *name,
    422                 const char *type)
    423 {
    424   static struct
    425   {
    426     const char *type;
    427     const char *suffix;
    428   } type_map [] = {
    429     { .type = "string",
    430       .suffix = "entry" },
    431     { .type = "date",
    432       .suffix = "cal" },
    433     { .type = NULL,
    434       .suffix = NULL }
    435   };
    436   char *data_widget;
    437 
    438   for (unsigned int i = 0; NULL != type_map[i].type; i++)
    439   {
    440     if (0 != strcmp (type_map[i].type,
    441                      type))
    442       continue;
    443     GNUNET_asprintf (&data_widget,
    444                      "%s_%s",
    445                      name,
    446                      type_map[i].suffix);
    447     return data_widget;
    448   }
    449   return NULL;
    450 }
    451 
    452 
    453 /**
    454  * Show widget on focus-in event.
    455  *
    456  * @param w widget to show (called with 'swapped' true!)
    457  * @param event the event
    458  * @param widget the widget being focused on
    459  * @return FALSE
    460  */
    461 bool
    462 anastasis_gtk_show_on_focus_in_event_cb (GtkWidget *w,
    463                                          GdkEvent *event,
    464                                          GtkWidget *widget)
    465 {
    466   gtk_widget_show (w);
    467   return FALSE;
    468 }
    469 
    470 
    471 /**
    472  * Hide widget on focus-out event.
    473  *
    474  * @param w widget to show (called with 'swapped' true!)
    475  * @param event the event
    476  * @param widget the widget being focused on
    477  * @return FALSE
    478  */
    479 bool
    480 anastasis_gtk_hide_on_focus_out_event_cb (GtkWidget *w,
    481                                           GdkEvent *event,
    482                                           GtkWidget *widget)
    483 {
    484   gtk_widget_hide (w);
    485   return FALSE;
    486 }