Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
spice_plugin_file_transfer.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2016-2018 Denis Ollier
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give
21  * permission to link the code of portions of this program with the
22  * OpenSSL library under certain conditions as described in each
23  * individual source file, and distribute linked combinations
24  * including the two.
25  * You must obey the GNU General Public License in all respects
26  * for all of the code used other than OpenSSL. * If you modify
27  * file(s) with this exception, you may extend this exception to your
28  * version of the file(s), but you are not obligated to do so. * If you
29  * do not wish to do so, delete this exception statement from your
30  * version. * If you delete this exception statement from all source
31  * files in the program, then also delete it here.
32  *
33  */
34 
35 #include "spice_plugin.h"
36 
37 #ifdef SPICE_GTK_CHECK_VERSION
38 # if SPICE_GTK_CHECK_VERSION(0, 31, 0)
39 
40 static void remmina_plugin_spice_file_transfer_cancel_cb(GtkButton *, SpiceFileTransferTask *);
42 static void remmina_plugin_spice_file_transfer_finished_cb(SpiceFileTransferTask *, GError *, RemminaProtocolWidget *);
43 static void remmina_plugin_spice_file_transfer_progress_cb(GObject *, GParamSpec *, RemminaProtocolWidget *);
44 
46  GtkWidget *vbox;
47  GtkWidget *hbox;
48  GtkWidget *progress;
49  GtkWidget *label;
50  GtkWidget *cancel;
52 
55 
56 void remmina_plugin_spice_file_transfer_new_cb(SpiceMainChannel *main_channel, SpiceFileTransferTask *task, RemminaProtocolWidget *gp)
57 {
58  TRACE_CALL(__func__);
59 
60  GtkWidget *dialog_content;
62  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
63 
64  g_signal_connect(task,
65  "finished",
67  gp);
68 
69  if (!gpdata->file_transfers) {
70  gpdata->file_transfers = g_hash_table_new_full(g_direct_hash,
71  g_direct_equal,
72  g_object_unref,
74  }
75 
76  if (!gpdata->file_transfer_dialog) {
77  /*
78  * FIXME: Use the RemminaConnectionWindow as transient parent widget
79  * (and add the GTK_DIALOG_DESTROY_WITH_PARENT flag) if it becomes
80  * accessible from the Remmina plugin API.
81  */
82  gpdata->file_transfer_dialog = gtk_dialog_new_with_buttons(_("File Transfers"),
83  NULL, 0,
84  _("_Cancel"),
85  GTK_RESPONSE_CANCEL,
86  NULL);
87  dialog_content = gtk_dialog_get_content_area(GTK_DIALOG(gpdata->file_transfer_dialog));
88  gtk_widget_set_size_request(dialog_content, 400, -1);
89  gtk_window_set_resizable(GTK_WINDOW(gpdata->file_transfer_dialog), FALSE);
90  g_signal_connect(gpdata->file_transfer_dialog,
91  "response",
93  gp);
94  }
95 
97  g_hash_table_insert(gpdata->file_transfers, g_object_ref(task), widgets);
98 
99  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(gpdata->file_transfer_dialog))),
100  widgets->vbox,
101  TRUE, TRUE, 6);
102 
103  g_signal_connect(task,
104  "notify::progress",
106  gp);
107 
108  gtk_widget_show(gpdata->file_transfer_dialog);
109 }
110 
112 {
113  TRACE_CALL(__func__);
114 
115  gchar *filename;
117 
118  widgets->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
119  widgets->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
120 
121  filename = spice_file_transfer_task_get_filename(task);
122  widgets->label = gtk_label_new(filename);
123  gtk_widget_set_halign(widgets->label, GTK_ALIGN_START);
124  gtk_widget_set_valign(widgets->label, GTK_ALIGN_BASELINE);
125 
126  widgets->progress = gtk_progress_bar_new();
127  gtk_widget_set_hexpand(widgets->progress, TRUE);
128  gtk_widget_set_valign(widgets->progress, GTK_ALIGN_CENTER);
129 
130  widgets->cancel = gtk_button_new_from_icon_name("gtk-cancel", GTK_ICON_SIZE_SMALL_TOOLBAR);
131  g_signal_connect(widgets->cancel,
132  "clicked",
134  task);
135  gtk_widget_set_hexpand(widgets->cancel, FALSE);
136  gtk_widget_set_valign(widgets->cancel, GTK_ALIGN_CENTER);
137 
138  gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->progress,
139  TRUE, TRUE, 0);
140  gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->cancel,
141  FALSE, TRUE, 0);
142 
143  gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->label,
144  TRUE, TRUE, 0);
145  gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->hbox,
146  TRUE, TRUE, 0);
147 
148  gtk_widget_show_all(widgets->vbox);
149 
150  g_free(filename);
151 
152  return widgets;
153 }
154 
156 {
157  TRACE_CALL(__func__);
158 
159  /* Child widgets will be destroyed automatically */
160  gtk_widget_destroy(widgets->vbox);
161  g_free(widgets);
162 }
163 
164 static void remmina_plugin_spice_file_transfer_cancel_cb(GtkButton *button, SpiceFileTransferTask *task)
165 {
166  TRACE_CALL(__func__);
167 
168  spice_file_transfer_task_cancel(task);
169 }
170 
171 
172 static void remmina_plugin_spice_file_transfer_dialog_response_cb(GtkDialog *dialog, gint response, RemminaProtocolWidget *gp)
173 {
174  TRACE_CALL(__func__);
175 
176  GHashTableIter iter;
177  gpointer key, value;
178  SpiceFileTransferTask *task;
179  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
180 
181  if (response == GTK_RESPONSE_CANCEL) {
182  g_hash_table_iter_init(&iter, gpdata->file_transfers);
183  while (g_hash_table_iter_next(&iter, &key, &value)) {
184  task = key;
185  spice_file_transfer_task_cancel(task);
186  }
187  }
188 }
189 
190 static void remmina_plugin_spice_file_transfer_progress_cb(GObject *task, GParamSpec *param_spec, RemminaProtocolWidget *gp)
191 {
192  TRACE_CALL(__func__);
193 
195  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
196 
197  widgets = g_hash_table_lookup(gpdata->file_transfers, task);
198  if (widgets) {
199  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(widgets->progress),
200  spice_file_transfer_task_get_progress(SPICE_FILE_TRANSFER_TASK(task)));
201  }
202 }
203 
204 static void remmina_plugin_spice_file_transfer_finished_cb(SpiceFileTransferTask *task, GError *error, RemminaProtocolWidget *gp)
205 {
206  TRACE_CALL(__func__);
207 
208  gchar *filename, *notification_message;
209  GNotification *notification;
210  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
211 
212  /*
213  * Send a desktop notification to inform about the outcome of
214  * the file transfer.
215  */
216  filename = spice_file_transfer_task_get_filename(task);
217 
218  if (error) {
219  notification = g_notification_new(_("Transfer error"));
220  notification_message = g_strdup_printf(_("%s: %s"),
221  filename, error->message);
222  }else {
223  notification = g_notification_new(_("Transfer completed"));
224  notification_message = g_strdup_printf(_("The %s file has been transferred"),
225  filename);
226  }
227 
228  g_notification_set_body(notification, notification_message);
229  g_application_send_notification(g_application_get_default(),
230  "remmina-plugin-spice-file-transfer-finished",
231  notification);
232 
233  g_hash_table_remove(gpdata->file_transfers, task);
234 
235  if (!g_hash_table_size(gpdata->file_transfers)) {
236  gtk_widget_hide(gpdata->file_transfer_dialog);
237  }
238 
239  g_free(filename);
240  g_free(notification_message);
241  g_object_unref(notification);
242 }
243 # endif /* SPICE_GTK_CHECK_VERSION(0, 31, 0) */
244 #endif /* SPICE_GTK_CHECK_VERSION */
static void remmina_plugin_spice_file_transfer_finished_cb(SpiceFileTransferTask *, GError *, RemminaProtocolWidget *)
void remmina_plugin_spice_file_transfer_new_cb(SpiceMainChannel *main_channel, SpiceFileTransferTask *task, RemminaProtocolWidget *gp)
static RemminaPluginSpiceXferWidgets * remmina_plugin_spice_xfer_widgets_new(SpiceFileTransferTask *)
static void remmina_plugin_spice_file_transfer_dialog_response_cb(GtkDialog *, gint, RemminaProtocolWidget *)
GHashTable * file_transfers
Definition: spice_plugin.h:64
static void remmina_plugin_spice_xfer_widgets_free(RemminaPluginSpiceXferWidgets *widgets)
GtkWidget * file_transfer_dialog
Definition: spice_plugin.h:65
struct _RemminaPluginSpiceXferWidgets RemminaPluginSpiceXferWidgets
static void remmina_plugin_spice_file_transfer_cancel_cb(GtkButton *, SpiceFileTransferTask *)
static void remmina_plugin_spice_file_transfer_progress_cb(GObject *, GParamSpec *, RemminaProtocolWidget *)