messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

send_file.c (9547B)


      1 /*
      2    This file is part of GNUnet.
      3    Copyright (C) 2021--2024 GNUnet e.V.
      4 
      5    GNUnet is free software: you can redistribute it and/or modify it
      6    under the terms of the GNU Affero General Public License as published
      7    by the Free Software Foundation, either version 3 of the License,
      8    or (at your option) any later version.
      9 
     10    GNUnet 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    Affero General Public License for more details.
     14 
     15    You should have received a copy of the GNU Affero General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18    SPDX-License-Identifier: AGPL3.0-or-later
     19  */
     20 /*
     21  * @author Tobias Frisch
     22  * @file ui/send_file.c
     23  */
     24 
     25 #include "send_file.h"
     26 
     27 #include "chat_entry.h"
     28 #include "chat_title.h"
     29 #include "file_load_entry.h"
     30 
     31 #include "../application.h"
     32 #include "../file.h"
     33 #include "../ui.h"
     34 
     35 static void
     36 handle_cancel_button_click(UNUSED GtkButton *button,
     37                            gpointer user_data)
     38 {
     39   g_assert(user_data);
     40 
     41   GtkDialog *dialog = GTK_DIALOG(user_data);
     42   gtk_window_close(GTK_WINDOW(dialog));
     43 }
     44 
     45 static void
     46 handle_sending_upload_file(UNUSED void *cls,
     47                            struct GNUNET_CHAT_File *file,
     48                            uint64_t completed,
     49                            uint64_t size)
     50 {
     51   g_assert(file);
     52 
     53   UI_FILE_LOAD_ENTRY_Handle *file_load = cls;
     54 
     55   gtk_progress_bar_set_fraction(
     56       file_load->load_progress_bar,
     57       1.0 * completed / size
     58   );
     59 
     60   file_update_upload_info(file, completed, size);
     61 
     62   if ((completed >= size) && (file_load->chat_title))
     63     ui_chat_title_remove_file_load(file_load->chat_title, file_load);
     64 }
     65 
     66 static void
     67 handle_send_button_click(GtkButton *button,
     68                          gpointer user_data)
     69 {
     70   g_assert((button) && (user_data));
     71 
     72   MESSENGER_Application *app = (MESSENGER_Application*) user_data;
     73 
     74   GtkTextView *text_view = GTK_TEXT_VIEW(
     75     g_object_get_qdata(G_OBJECT(button), app->quarks.widget)
     76   );
     77 
     78   if (!text_view)
     79     return;
     80 
     81   gchar *filename = gtk_file_chooser_get_filename(
     82     GTK_FILE_CHOOSER(app->ui.send_file.file_chooser_button)
     83   );
     84 
     85   if (!filename)
     86     return;
     87 
     88   struct GNUNET_CHAT_Context *context = (struct GNUNET_CHAT_Context*) (
     89     g_object_get_qdata(G_OBJECT(text_view), app->quarks.data)
     90   );
     91 
     92   UI_CHAT_ENTRY_Handle *entry = GNUNET_CHAT_context_get_user_pointer(context);
     93   UI_CHAT_Handle *handle = entry? entry->chat : NULL;
     94 
     95   UI_FILE_LOAD_ENTRY_Handle *file_load = NULL;
     96   struct GNUNET_CHAT_File *file = NULL;
     97 
     98   if ((context) && (handle))
     99   {
    100     file_load = ui_file_load_entry_new(app);
    101 
    102     gtk_label_set_text(file_load->file_label, filename);
    103     gtk_progress_bar_set_fraction(file_load->load_progress_bar, 0.0);
    104 
    105     application_chat_lock(app);
    106 
    107     file = GNUNET_CHAT_context_send_file(
    108       context,
    109       filename,
    110       handle_sending_upload_file,
    111       file_load
    112     );
    113 
    114     application_chat_unlock(app);
    115   }
    116 
    117   g_free(filename);
    118 
    119   gtk_window_close(GTK_WINDOW(app->ui.send_file.dialog));
    120 
    121   if (!file)
    122   {
    123     if (file_load)
    124       ui_file_load_entry_delete(file_load);
    125 
    126     return;
    127   }
    128 
    129   file_create_info(file);
    130 
    131   ui_chat_title_add_file_load(handle->title, file_load);
    132 }
    133 
    134 static void
    135 handle_dialog_destroy(UNUSED GtkWidget *window,
    136                       gpointer user_data)
    137 {
    138   g_assert(user_data);
    139 
    140   ui_send_file_dialog_cleanup((UI_SEND_FILE_Handle*) user_data);
    141 }
    142 
    143 static int
    144 handle_file_redraw_animation(gpointer user_data)
    145 {
    146   g_assert(user_data);
    147 
    148   UI_SEND_FILE_Handle *handle = (UI_SEND_FILE_Handle*) user_data;
    149 
    150   handle->redraw_animation = 0;
    151 
    152   if ((handle->file_drawing_area) &&
    153       ((handle->image) || (handle->animation) || (handle->animation_iter)))
    154     gtk_widget_queue_draw(GTK_WIDGET(handle->file_drawing_area));
    155 
    156   return FALSE;
    157 }
    158 
    159 static gboolean
    160 handle_file_drawing_area_draw(GtkWidget* drawing_area,
    161                               cairo_t* cairo,
    162                               gpointer user_data)
    163 {
    164   g_assert((drawing_area) && (cairo) && (user_data));
    165 
    166   UI_SEND_FILE_Handle *handle = (UI_SEND_FILE_Handle*) user_data;
    167 
    168   GtkStyleContext* context = gtk_widget_get_style_context(drawing_area);
    169 
    170   const guint width = gtk_widget_get_allocated_width(drawing_area);
    171   const guint height = gtk_widget_get_allocated_height(drawing_area);
    172 
    173   gtk_render_background(context, cairo, 0, 0, width, height);
    174 
    175   GdkPixbuf *image = handle->image;
    176 
    177   if (!(handle->animation))
    178     goto render_image;
    179 
    180   if (handle->animation_iter)
    181     gdk_pixbuf_animation_iter_advance(handle->animation_iter, NULL);
    182   else
    183     handle->animation_iter = gdk_pixbuf_animation_get_iter(
    184 	    handle->animation, NULL
    185     );
    186 
    187   image = gdk_pixbuf_animation_iter_get_pixbuf(handle->animation_iter);
    188 
    189   const int delay = gdk_pixbuf_animation_iter_get_delay_time(
    190     handle->animation_iter
    191   );
    192 
    193   handle->redraw_animation = util_timeout_add(
    194     delay,
    195     handle_file_redraw_animation,
    196     handle
    197   );
    198 
    199 render_image:
    200   if (!image)
    201     return FALSE;
    202 
    203   int dwidth = gdk_pixbuf_get_width(image);
    204   int dheight = gdk_pixbuf_get_height(image);
    205 
    206   double ratio_width = 1.0 * width / dwidth;
    207   double ratio_height = 1.0 * height / dheight;
    208 
    209   const double ratio = ratio_width < ratio_height? ratio_width : ratio_height;
    210 
    211   dwidth = (int) (dwidth * ratio);
    212   dheight = (int) (dheight * ratio);
    213 
    214   double dx = (width - dwidth) * 0.5;
    215   double dy = (height - dheight) * 0.5;
    216 
    217   const int interp_type = (ratio >= 1.0?
    218     GDK_INTERP_NEAREST :
    219     GDK_INTERP_BILINEAR
    220   );
    221 
    222   GdkPixbuf* scaled = gdk_pixbuf_scale_simple(
    223     image,
    224     dwidth,
    225     dheight,
    226     interp_type
    227   );
    228 
    229   gtk_render_icon(context, cairo, scaled, dx, dy);
    230 
    231   cairo_fill(cairo);
    232 
    233   g_object_unref(scaled);
    234   return FALSE;
    235 }
    236 
    237 static void
    238 _clear_file_preview_data(UI_SEND_FILE_Handle *handle)
    239 {
    240   g_assert(handle);
    241 
    242   if (handle->image)
    243   {
    244     g_object_unref(handle->image);
    245     handle->image = NULL;
    246   }
    247 
    248   if (handle->redraw_animation)
    249   {
    250     util_source_remove(handle->redraw_animation);
    251     handle->redraw_animation = 0;
    252   }
    253 
    254   if (handle->animation_iter)
    255   {
    256     g_object_unref(handle->animation_iter);
    257     handle->animation_iter = NULL;
    258   }
    259 
    260   if (handle->animation)
    261   {
    262     g_object_unref(handle->animation);
    263     handle->animation = NULL;
    264   }
    265 }
    266 
    267 static void
    268 handle_file_chooser_button_file_set(GtkFileChooserButton *file_chooser_button,
    269                                     gpointer user_data)
    270 {
    271   g_assert((file_chooser_button) && (user_data));
    272 
    273   UI_SEND_FILE_Handle *handle = (UI_SEND_FILE_Handle*) user_data;
    274 
    275   _clear_file_preview_data(handle);
    276 
    277   char *filename = gtk_file_chooser_get_filename(
    278       GTK_FILE_CHOOSER(file_chooser_button)
    279   );
    280 
    281   if (filename)
    282   {
    283     handle->animation = gdk_pixbuf_animation_new_from_file(filename, NULL);
    284 
    285     if (!(handle->animation))
    286       handle->image = gdk_pixbuf_new_from_file(filename, NULL);
    287 
    288     g_free(filename);
    289   }
    290 
    291   if (handle->file_drawing_area)
    292     gtk_widget_queue_draw(GTK_WIDGET(handle->file_drawing_area));
    293 }
    294 
    295 void
    296 ui_send_file_dialog_init(MESSENGER_Application *app,
    297                          UI_SEND_FILE_Handle *handle)
    298 {
    299   g_assert((app) && (handle));
    300 
    301   handle->builder = ui_builder_from_resource(
    302     application_get_resource_path(app, "ui/send_file.ui")
    303   );
    304 
    305   handle->dialog = GTK_DIALOG(
    306     gtk_builder_get_object(handle->builder, "send_file_dialog")
    307   );
    308 
    309   gtk_window_set_title(
    310     GTK_WINDOW(handle->dialog),
    311     _("Send File")
    312   );
    313 
    314   gtk_window_set_transient_for(
    315     GTK_WINDOW(handle->dialog),
    316     GTK_WINDOW(app->ui.messenger.main_window)
    317   );
    318 
    319   handle->file_drawing_area = GTK_DRAWING_AREA(
    320     gtk_builder_get_object(handle->builder, "file_drawing_area")
    321   );
    322 
    323   handle->file_chooser_button = GTK_FILE_CHOOSER_BUTTON(
    324     gtk_builder_get_object(handle->builder, "file_chooser_button")
    325   );
    326 
    327   handle->file_draw_signal = g_signal_connect(
    328     handle->file_drawing_area,
    329     "draw",
    330     G_CALLBACK(handle_file_drawing_area_draw),
    331     handle
    332   );
    333 
    334   g_signal_connect(
    335     handle->file_chooser_button,
    336     "file-set",
    337     G_CALLBACK(handle_file_chooser_button_file_set),
    338     handle
    339   );
    340 
    341   handle->cancel_button = GTK_BUTTON(
    342     gtk_builder_get_object(handle->builder, "cancel_button")
    343   );
    344 
    345   g_signal_connect(
    346     handle->cancel_button,
    347     "clicked",
    348     G_CALLBACK(handle_cancel_button_click),
    349     handle->dialog
    350   );
    351 
    352   handle->send_button = GTK_BUTTON(
    353     gtk_builder_get_object(handle->builder, "send_button")
    354   );
    355 
    356   g_signal_connect(
    357     handle->send_button,
    358     "clicked",
    359     G_CALLBACK(handle_send_button_click),
    360     app
    361   );
    362 
    363   g_signal_connect(
    364     handle->dialog,
    365     "destroy",
    366     G_CALLBACK(handle_dialog_destroy),
    367     handle
    368   );
    369 
    370   handle->image = NULL;
    371   handle->animation = NULL;
    372   handle->animation_iter = NULL;
    373 
    374   handle->redraw_animation = 0;
    375 }
    376 
    377 void
    378 ui_send_file_dialog_update(UI_SEND_FILE_Handle *handle,
    379                            const gchar *filename)
    380 {
    381   g_assert((handle) && (filename));
    382 
    383   if (!(handle->file_chooser_button))
    384     return;
    385 
    386   gtk_file_chooser_set_filename(
    387     GTK_FILE_CHOOSER(handle->file_chooser_button),
    388     filename
    389   );
    390 
    391   handle_file_chooser_button_file_set(
    392     handle->file_chooser_button,
    393     handle
    394   );
    395 }
    396 
    397 void
    398 ui_send_file_dialog_cleanup(UI_SEND_FILE_Handle *handle)
    399 {
    400   g_assert(handle);
    401 
    402   _clear_file_preview_data(handle);
    403 
    404   g_signal_handler_disconnect(
    405       handle->file_drawing_area,
    406       handle->file_draw_signal
    407   );
    408 
    409   g_object_unref(handle->builder);
    410 
    411   memset(handle, 0, sizeof(*handle));
    412 }