anastasis-gtk_cells.c (15277B)
1 /* 2 This file is part of anastasis-gtk. 3 Copyright (C) 2025 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 * @file src/anastasis/anastasis-gtk_cells.c 22 * @brief Widgets rendering the cells of our GtkColumnViews 23 * @author Christian Grothoff 24 */ 25 #include <gnunet/gnunet_util_lib.h> 26 #include "anastasis_gtk_util.h" 27 #include "anastasis-gtk_helper.h" 28 #include "anastasis-gtk_rows.h" 29 30 31 /** 32 * Padding above and below the rows of the two progress lists. 33 */ 34 #define PROGRESS_PADDING 15 35 36 37 /** 38 * Return the row the cell of @a item shows and remember it on the 39 * widget of that cell, so that a right-click can find it again. 40 * 41 * @param item the cell being bound 42 * @return NULL if @a item shows no row 43 */ 44 static AGRow * 45 bind_row (GtkListItem *item) 46 { 47 AGRow *row = AG_row_of (gtk_list_item_get_item (item)); 48 GtkWidget *child = gtk_list_item_get_child (item); 49 50 if (NULL != child) 51 AG_cell_set_row (child, 52 row); 53 return row; 54 } 55 56 57 /** 58 * Set the label of the cell of @a item to the @a field of its row. 59 * 60 * @param item the cell being bound 61 * @param field field of the row to show 62 */ 63 static void 64 bind_text (GtkListItem *item, 65 const char *field) 66 { 67 AGRow *row = bind_row (item); 68 const char *val = NULL; 69 70 if (NULL != row) 71 val = AG_row_string (row, 72 field); 73 gtk_label_set_text (GTK_LABEL (gtk_list_item_get_child (item)), 74 (NULL != val) ? val : ""); 75 } 76 77 78 /** 79 * Define the "bind" handler @a name of a column showing the string 80 * @a field of the rows. The columns of our lists differ only in the 81 * field they show, but the UI files must name a distinct handler for 82 * each of them. 83 * 84 * @param name name of the handler to define 85 * @param field field of the row the column shows 86 */ 87 #define TEXT_CELL(name, field) \ 88 void \ 89 name (GtkSignalListItemFactory *f, \ 90 GtkListItem *item, \ 91 gpointer user_data) \ 92 { \ 93 (void) f; \ 94 (void) user_data; \ 95 bind_text (item, \ 96 field); \ 97 } 98 99 100 /** 101 * Create the label rendering a cell of a text column. 102 * 103 * @param f the factory of the column 104 * @param item the cell to fill 105 * @param user_data the builder 106 */ 107 void 108 AG_cell_setup_text (GtkSignalListItemFactory *f, 109 GtkListItem *item, 110 gpointer user_data) 111 { 112 GtkWidget *label; 113 114 (void) f; 115 (void) user_data; 116 label = gtk_label_new (NULL); 117 gtk_label_set_xalign (GTK_LABEL (label), 118 0.0); 119 gtk_widget_set_halign (label, 120 GTK_ALIGN_START); 121 gtk_list_item_set_child (item, 122 label); 123 } 124 125 126 /** 127 * Forget the row a cell showed once it is no longer shown. 128 * 129 * @param f the factory of the column 130 * @param item the cell that was unbound 131 * @param user_data the builder 132 */ 133 void 134 AG_cell_unbind (GtkSignalListItemFactory *f, 135 GtkListItem *item, 136 gpointer user_data) 137 { 138 GtkWidget *child = gtk_list_item_get_child (item); 139 140 (void) f; 141 (void) user_data; 142 if (NULL != child) 143 AG_cell_set_row (child, 144 NULL); 145 } 146 147 148 /* ****************** progress lists ******************* */ 149 150 151 /** 152 * Create the label rendering a step of one of the progress lists. 153 * 154 * @param f the factory of the column 155 * @param item the cell to fill 156 * @param user_data the builder 157 */ 158 void 159 AG_cell_setup_progress (GtkSignalListItemFactory *f, 160 GtkListItem *item, 161 gpointer user_data) 162 { 163 GtkWidget *label; 164 165 AG_cell_setup_text (f, 166 item, 167 user_data); 168 label = gtk_list_item_get_child (item); 169 gtk_widget_set_margin_top (label, 170 PROGRESS_PADDING); 171 gtk_widget_set_margin_bottom (label, 172 PROGRESS_PADDING); 173 } 174 175 176 /** 177 * Show the description of a step of one of the progress lists, using the 178 * long explanation as the tooltip. 179 * 180 * @param f the factory of the column 181 * @param item the cell to fill 182 * @param user_data the builder 183 */ 184 void 185 AG_cell_bind_progress (GtkSignalListItemFactory *f, 186 GtkListItem *item, 187 gpointer user_data) 188 { 189 AGRow *row; 190 191 (void) f; 192 (void) user_data; 193 bind_text (item, 194 "description"); 195 row = AG_row_of (gtk_list_item_get_item (item)); 196 if (NULL == row) 197 return; 198 gtk_widget_set_tooltip_text (gtk_list_item_get_child (item), 199 AG_row_string (row, 200 "tooltip")); 201 } 202 203 204 /* ************ continents, countries, methods ************* */ 205 206 207 TEXT_CELL (AG_cell_bind_continent, "name_i18n") 208 TEXT_CELL (AG_cell_bind_country, "name") 209 TEXT_CELL (AG_cell_bind_auth_type, "type") 210 TEXT_CELL (AG_cell_bind_auth_details, "visualization") 211 212 213 /* ******************* policy review ******************** */ 214 215 216 /** 217 * Create the expander and label rendering the first column of the policy 218 * review, where the challenges are nested under their policy. 219 * 220 * @param f the factory of the column 221 * @param item the cell to fill 222 * @param user_data the builder 223 */ 224 void 225 AG_cell_setup_policy_name (GtkSignalListItemFactory *f, 226 GtkListItem *item, 227 gpointer user_data) 228 { 229 GtkWidget *expander; 230 GtkWidget *label; 231 232 (void) f; 233 (void) user_data; 234 label = gtk_label_new (NULL); 235 gtk_label_set_xalign (GTK_LABEL (label), 236 0.0); 237 gtk_widget_set_halign (label, 238 GTK_ALIGN_START); 239 expander = gtk_tree_expander_new (); 240 gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), 241 label); 242 gtk_list_item_set_child (item, 243 expander); 244 } 245 246 247 /** 248 * Show the name of a policy, or the instructions of a challenge. 249 * 250 * @param f the factory of the column 251 * @param item the cell to fill 252 * @param user_data the builder 253 */ 254 void 255 AG_cell_bind_policy_name (GtkSignalListItemFactory *f, 256 GtkListItem *item, 257 gpointer user_data) 258 { 259 GtkTreeExpander *expander; 260 GtkWidget *label; 261 AGRow *row; 262 const char *name = NULL; 263 264 (void) f; 265 (void) user_data; 266 expander = GTK_TREE_EXPANDER (gtk_list_item_get_child (item)); 267 gtk_tree_expander_set_list_row (expander, 268 GTK_TREE_LIST_ROW ( 269 gtk_list_item_get_item (item))); 270 row = bind_row (item); 271 if (NULL != row) 272 name = AG_row_string (row, 273 "policy_name"); 274 label = gtk_tree_expander_get_child (expander); 275 gtk_label_set_text (GTK_LABEL (label), 276 (NULL != name) ? name : ""); 277 } 278 279 280 TEXT_CELL (AG_cell_bind_policy_type, "method_type") 281 TEXT_CELL (AG_cell_bind_policy_cost, "cost") 282 TEXT_CELL (AG_cell_bind_policy_provider, "provider_name") 283 284 285 /* ****************** payment QR codes ******************* */ 286 287 288 /** 289 * Create the picture rendering a QR code. 290 * 291 * @param f the factory of the column 292 * @param item the cell to fill 293 * @param user_data the builder 294 */ 295 void 296 AG_cell_setup_qr_image (GtkSignalListItemFactory *f, 297 GtkListItem *item, 298 gpointer user_data) 299 { 300 GtkWidget *picture; 301 302 (void) f; 303 (void) user_data; 304 picture = gtk_picture_new (); 305 /* render the QR code at its natural size */ 306 gtk_picture_set_can_shrink (GTK_PICTURE (picture), 307 FALSE); 308 gtk_picture_set_content_fit (GTK_PICTURE (picture), 309 GTK_CONTENT_FIT_SCALE_DOWN); 310 /* a GtkCellRendererPixbuf centred its image in the cell */ 311 gtk_widget_set_halign (picture, 312 GTK_ALIGN_CENTER); 313 gtk_list_item_set_child (item, 314 picture); 315 } 316 317 318 /** 319 * Show the QR code of a payment request. 320 * 321 * @param f the factory of the column 322 * @param item the cell to fill 323 * @param user_data the builder 324 */ 325 void 326 AG_cell_bind_qr_image (GtkSignalListItemFactory *f, 327 GtkListItem *item, 328 gpointer user_data) 329 { 330 AGRow *row = bind_row (item); 331 332 (void) f; 333 (void) user_data; 334 gtk_picture_set_paintable (GTK_PICTURE (gtk_list_item_get_child (item)), 335 (NULL != row) 336 ? AG_row_image (row) 337 : NULL); 338 } 339 340 341 TEXT_CELL (AG_cell_bind_qr_uri, "url") 342 343 344 /* ***************** completed backup ******************* */ 345 346 347 TEXT_CELL (AG_cell_bind_backup_provider, "provider_name") 348 349 350 /** 351 * Show the version of the policy stored at a provider, but only for the 352 * providers the backup actually reached. 353 * 354 * @param f the factory of the column 355 * @param item the cell to fill 356 * @param user_data the builder 357 */ 358 void 359 AG_cell_bind_backup_version (GtkSignalListItemFactory *f, 360 GtkListItem *item, 361 gpointer user_data) 362 { 363 AGRow *row = bind_row (item); 364 GtkWidget *label = gtk_list_item_get_child (item); 365 char *version; 366 367 (void) f; 368 (void) user_data; 369 if (NULL == row) 370 { 371 gtk_widget_set_visible (label, 372 FALSE); 373 return; 374 } 375 GNUNET_asprintf (&version, 376 "%llu", 377 (unsigned long long) AG_row_uint (row, 378 "version")); 379 gtk_label_set_text (GTK_LABEL (label), 380 version); 381 GNUNET_free (version); 382 gtk_widget_set_visible (label, 383 AG_row_bool (row, 384 "success")); 385 } 386 387 388 /* ***************** secret selection ******************* */ 389 390 391 TEXT_CELL (AG_cell_bind_secret_name, "secret_name") 392 TEXT_CELL (AG_cell_bind_secret_time, "date_string") 393 394 395 /* ****************** provider list ******************** */ 396 397 398 /** 399 * The user toggled whether a provider is to be used. 400 * 401 * @param button the check button of the row 402 * @param user_data NULL 403 */ 404 static void 405 provider_use_toggled_cb (GtkCheckButton *button, 406 gpointer user_data) 407 { 408 AGRow *row; 409 bool active; 410 411 (void) user_data; 412 row = AG_cell_get_row (GTK_WIDGET (button)); 413 if (NULL == row) 414 return; /* not bound to a row (yet) */ 415 active = gtk_check_button_get_active (button); 416 if (active == AG_row_bool (row, 417 "enabled")) 418 return; /* we set the state ourselves while binding */ 419 AG_row_set (row, 420 "enabled", 421 json_boolean (active)); 422 } 423 424 425 /** 426 * Create the check button with which a provider is enabled or disabled. 427 * 428 * @param f the factory of the column 429 * @param item the cell to fill 430 * @param user_data the builder 431 */ 432 void 433 AG_cell_setup_provider_use (GtkSignalListItemFactory *f, 434 GtkListItem *item, 435 gpointer user_data) 436 { 437 GtkWidget *check; 438 439 (void) f; 440 (void) user_data; 441 check = gtk_check_button_new (); 442 gtk_widget_set_halign (check, 443 GTK_ALIGN_CENTER); 444 g_signal_connect (check, 445 "toggled", 446 G_CALLBACK (&provider_use_toggled_cb), 447 NULL); 448 gtk_list_item_set_child (item, 449 check); 450 } 451 452 453 /** 454 * Show whether a provider is to be used. Providers we could not talk to 455 * cannot be enabled, which is shown as an indeterminate state. 456 * 457 * @param f the factory of the column 458 * @param item the cell to fill 459 * @param user_data the builder 460 */ 461 void 462 AG_cell_bind_provider_use (GtkSignalListItemFactory *f, 463 GtkListItem *item, 464 gpointer user_data) 465 { 466 GtkCheckButton *check; 467 AGRow *row; 468 469 (void) f; 470 (void) user_data; 471 /* the row must be known before we change the state, or the "toggled" 472 handler cannot tell our own change from the user's */ 473 row = bind_row (item); 474 check = GTK_CHECK_BUTTON (gtk_list_item_get_child (item)); 475 if (NULL == row) 476 return; 477 gtk_check_button_set_active (check, 478 AG_row_bool (row, 479 "enabled")); 480 gtk_check_button_set_inconsistent (check, 481 ! AG_row_bool (row, 482 "sensitive")); 483 gtk_widget_set_sensitive (GTK_WIDGET (check), 484 AG_row_bool (row, 485 "sensitive")); 486 } 487 488 489 TEXT_CELL (AG_cell_bind_provider_name, "name") 490 491 492 /** 493 * Show the liability limit of a provider, which we only know for the 494 * providers we could talk to. 495 * 496 * @param f the factory of the column 497 * @param item the cell to fill 498 * @param user_data the builder 499 */ 500 void 501 AG_cell_bind_provider_liability (GtkSignalListItemFactory *f, 502 GtkListItem *item, 503 gpointer user_data) 504 { 505 AGRow *row; 506 507 (void) f; 508 (void) user_data; 509 bind_text (item, 510 "liability"); 511 row = AG_row_of (gtk_list_item_get_item (item)); 512 gtk_widget_set_visible (gtk_list_item_get_child (item), 513 (NULL != row) && 514 AG_row_bool (row, 515 "sensitive")); 516 } 517 518 519 /** 520 * Show the status of a provider in the colour that goes with it. 521 * 522 * @param f the factory of the column 523 * @param item the cell to fill 524 * @param user_data the builder 525 */ 526 void 527 AG_cell_bind_provider_status (GtkSignalListItemFactory *f, 528 GtkListItem *item, 529 gpointer user_data) 530 { 531 AGRow *row = bind_row (item); 532 GtkLabel *label = GTK_LABEL (gtk_list_item_get_child (item)); 533 const char *status; 534 const char *color; 535 536 (void) f; 537 (void) user_data; 538 status = (NULL != row) 539 ? AG_row_string (row, 540 "status") 541 : NULL; 542 if (NULL == status) 543 { 544 gtk_label_set_text (label, 545 ""); 546 return; 547 } 548 color = AG_row_string (row, 549 "color"); 550 if (NULL == color) 551 { 552 gtk_label_set_text (label, 553 status); 554 return; 555 } 556 { 557 char *markup; 558 559 markup = g_markup_printf_escaped ("<span foreground=\"%s\">%s</span>", 560 color, 561 status); 562 gtk_label_set_markup (label, 563 markup); 564 g_free (markup); 565 } 566 } 567 568 569 /* end of anastasis-gtk_cells.c */