anastasis-gtk

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

anastasis-gtk_handle-policy-meta.c (8750B)


      1 /*
      2      This file is part of anastasis-gtk.
      3      Copyright (C) 2021 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_handle-policy-meta.c
     23  * @brief Handle right-click context menu in policy review
     24  * @author Christian Grothoff
     25  */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include "anastasis_gtk_util.h"
     28 #include "anastasis-gtk_action.h"
     29 #include "anastasis-gtk_helper.h"
     30 #include "anastasis-gtk_pe.h"
     31 #include "anastasis-gtk_rows.h"
     32 #include <jansson.h>
     33 
     34 
     35 /**
     36  * Context for menu callbacks.
     37  */
     38 struct MenuContext
     39 {
     40   /**
     41    * The row that the user right-clicked, NULL if they clicked past the
     42    * last one.
     43    */
     44   AGRow *row;
     45 
     46 };
     47 
     48 
     49 /**
     50  * Free a `struct MenuContext`.  Called when the action group holding it goes
     51  * away, which happens on the next right-click at the latest.
     52  *
     53  * @param cls the `struct MenuContext` to free
     54  */
     55 static void
     56 free_menu_context (gpointer cls)
     57 {
     58   struct MenuContext *ctx = cls;
     59 
     60   g_clear_object (&ctx->row);
     61   GNUNET_free (ctx);
     62 }
     63 
     64 
     65 /**
     66  * The user selected the 'add policy' menu item.
     67  *
     68  * @param action the activated action
     69  * @param parameter unused
     70  * @param user_data a `struct MenuContext`
     71  */
     72 static void
     73 add_from_ctx_menu (GSimpleAction *action,
     74                    GVariant *parameter,
     75                    gpointer user_data)
     76 {
     77   (void) action;
     78   (void) parameter;
     79   (void) user_data;
     80   AG_add_policy ();
     81 }
     82 
     83 
     84 /**
     85  * The user selected the 'delete challenge' or 'delete policy' menu item.
     86  *
     87  * @param action the activated action
     88  * @param parameter unused
     89  * @param user_data a `struct MenuContext`
     90  */
     91 static void
     92 delete_from_ctx_menu (GSimpleAction *action,
     93                       GVariant *parameter,
     94                       gpointer user_data)
     95 {
     96   struct MenuContext *ctx = user_data;
     97   guint pindex;
     98 
     99   (void) action;
    100   (void) parameter;
    101   if (NULL == ctx->row)
    102     return;
    103   pindex = AG_row_uint (ctx->row,
    104                         "policy_index");
    105   if (AG_row_bool (ctx->row,
    106                    "is_challenge"))
    107     AG_delete_challenge (pindex,
    108                          AG_row_uint (ctx->row,
    109                                       "method_index"));
    110   else
    111     AG_delete_policy (pindex);
    112 }
    113 
    114 
    115 /**
    116  * The user selected the 'edit policy' menu item.
    117  *
    118  * @param action the activated action
    119  * @param parameter unused
    120  * @param user_data a `struct MenuContext`
    121  */
    122 static void
    123 edit_from_ctx_menu (GSimpleAction *action,
    124                     GVariant *parameter,
    125                     gpointer user_data)
    126 {
    127   struct MenuContext *ctx = user_data;
    128 
    129   (void) action;
    130   (void) parameter;
    131   if (NULL == ctx->row)
    132     return;
    133   AG_edit_policy (AG_row_uint (ctx->row,
    134                                "policy_index"));
    135 }
    136 
    137 
    138 /**
    139  * Build the menu model applicable to the row that was clicked.  The two
    140  * sections are rendered with a separator in between.
    141  *
    142  * @param have_row true if the user clicked on a row
    143  * @param is_challenge true if that row represents a challenge
    144  * @return the menu model
    145  */
    146 static GMenuModel *
    147 build_menu (bool have_row,
    148             bool is_challenge)
    149 {
    150   GMenu *menu = g_menu_new ();
    151 
    152   if (have_row)
    153   {
    154     GMenu *section = g_menu_new ();
    155 
    156     if (! is_challenge)
    157     {
    158       /* only show 'edit' entry for lines that are for an entire policy */
    159       g_menu_append (section,
    160                      _ ("_Edit policy..."),
    161                      "policy.edit");
    162     }
    163     g_menu_append (section,
    164                    is_challenge
    165                    ? _ ("Delete challenge")
    166                    : _ ("Delete policy"),
    167                    "policy.delete");
    168     g_menu_append_section (menu,
    169                            NULL,
    170                            G_MENU_MODEL (section));
    171     g_object_unref (section);
    172   }
    173   {
    174     GMenu *section = g_menu_new ();
    175 
    176     g_menu_append (section,
    177                    _ ("_Add policy..."),
    178                    "policy.add");
    179     g_menu_append_section (menu,
    180                            NULL,
    181                            G_MENU_MODEL (section));
    182     g_object_unref (section);
    183   }
    184   return G_MENU_MODEL (menu);
    185 }
    186 
    187 
    188 /**
    189  * Install the actions the context menu of @a tv activates; they operate on the
    190  * row given by @a ctx.
    191  *
    192  * @param tv the list the menu belongs to
    193  * @param ctx context to hand to the actions, ownership is taken
    194  */
    195 static void
    196 install_actions (GtkWidget *tv,
    197                  struct MenuContext *ctx)
    198 {
    199   static const GActionEntry entries[] = {
    200     { .name = "add",
    201       .activate = &add_from_ctx_menu },
    202     { .name = "edit",
    203       .activate = &edit_from_ctx_menu },
    204     { .name = "delete",
    205       .activate = &delete_from_ctx_menu }
    206   };
    207   GSimpleActionGroup *actions;
    208 
    209   actions = g_simple_action_group_new ();
    210   g_action_map_add_action_entries (G_ACTION_MAP (actions),
    211                                   entries,
    212                                   G_N_ELEMENTS (entries),
    213                                   ctx);
    214   /* the action group owns the context; installing a new group on the next
    215      right-click releases the previous one */
    216   g_object_set_data_full (G_OBJECT (actions),
    217                           "anastasis-menu-context",
    218                           ctx,
    219                           &free_menu_context);
    220   gtk_widget_insert_action_group (tv,
    221                                   "policy",
    222                                   G_ACTION_GROUP (actions));
    223   g_object_unref (actions);
    224 }
    225 
    226 
    227 /**
    228  * Return the context menu of @a tv, creating it on first use.  A single
    229  * popover is reused for the lifetime of the list; only its model and
    230  * position change.
    231  *
    232  * @param tv the list
    233  * @return the popover to show
    234  */
    235 static GtkPopover *
    236 get_popover (GtkWidget *tv)
    237 {
    238   GtkWidget *popover;
    239 
    240   popover = g_object_get_data (G_OBJECT (tv),
    241                                "anastasis-context-menu");
    242   if (NULL == popover)
    243   {
    244     popover = gtk_popover_menu_new_from_model (NULL);
    245     gtk_popover_set_has_arrow (GTK_POPOVER (popover),
    246                                FALSE);
    247     gtk_widget_set_halign (popover,
    248                            GTK_ALIGN_START);
    249     gtk_widget_set_parent (popover,
    250                            tv);
    251     g_object_set_data_full (G_OBJECT (tv),
    252                             "anastasis-context-menu",
    253                             popover,
    254                             (GDestroyNotify) & gtk_widget_unparent);
    255   }
    256   return GTK_POPOVER (popover);
    257 }
    258 
    259 
    260 /**
    261  * We got a right-click on the policy list: compute which menu items are
    262  * applicable and display an appropriate menu.
    263  *
    264  * @param gesture the click gesture, restricted to button 3 by the UI file
    265  * @param n_press number of presses
    266  * @param x horizontal position of the click, in widget coordinates
    267  * @param y vertical position of the click, in widget coordinates
    268  * @param user_data NULL
    269  */
    270 void
    271 anastasis_gtk_review_policy_treeview_pressed_cb (GtkGestureClick *gesture,
    272                                                  gint n_press,
    273                                                  gdouble x,
    274                                                  gdouble y,
    275                                                  gpointer user_data)
    276 {
    277   GtkWidget *widget
    278     = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
    279   struct MenuContext *ctx;
    280   AGRow *row;
    281   bool is_challenge = false;
    282 
    283   (void) n_press;
    284   (void) user_data;
    285   ctx = GNUNET_new (struct MenuContext);
    286   row = AG_row_at (widget,
    287                    y);
    288   if (NULL != row)
    289   {
    290     ctx->row = g_object_ref (row);
    291     is_challenge = AG_row_bool (row,
    292                                 "is_challenge");
    293   }
    294   install_actions (widget,
    295                    ctx);
    296   {
    297     GtkPopover *popover = get_popover (widget);
    298     GMenuModel *model = build_menu (NULL != row,
    299                                     is_challenge);
    300     const GdkRectangle at = {
    301       .x = (int) x,
    302       .y = (int) y,
    303       .width = 1,
    304       .height = 1
    305     };
    306 
    307     gtk_popover_menu_set_menu_model (GTK_POPOVER_MENU (popover),
    308                                      model);
    309     g_object_unref (model);
    310     gtk_popover_set_pointing_to (popover,
    311                                  &at);
    312     gtk_popover_popup (popover);
    313   }
    314 }