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.
remmina_ftp_client.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #define _FILE_OFFSET_BITS 64
38 
39 #include <gdk/gdk.h>
40 #include <gtk/gtk.h>
41 #include <glib/gi18n.h>
42 #include <glib/gstdio.h>
43 #include "config.h"
44 #include "remmina_public.h"
45 #include "remmina_pref.h"
46 #include "remmina_marshals.h"
47 #include "remmina_file.h"
48 #include "remmina_ftp_client.h"
51 
52 /* -------------------- RemminaCellRendererPixbuf ----------------------- */
53 /* A tiny cell renderer that extends the default pixbuf cell render to accept activation */
54 
55 #define REMMINA_TYPE_CELL_RENDERER_PIXBUF \
56  (remmina_cell_renderer_pixbuf_get_type())
57 #define REMMINA_CELL_RENDERER_PIXBUF(obj) \
58  (G_TYPE_CHECK_INSTANCE_CAST((obj), REMMINA_TYPE_CELL_RENDERER_PIXBUF, RemminaCellRendererPixbuf))
59 #define REMMINA_CELL_RENDERER_PIXBUF_CLASS(klass) \
60  (G_TYPE_CHECK_CLASS_CAST((klass), REMMINA_TYPE_CELL_RENDERER_PIXBUF, RemminaCellRendererPixbufClass))
61 #define REMMINA_IS_CELL_RENDERER_PIXBUF(obj) \
62  (G_TYPE_CHECK_INSTANCE_TYPE((obj), REMMINA_TYPE_CELL_RENDERER_PIXBUF))
63 
65  GtkCellRendererPixbuf renderer;
67 
69  GtkCellRendererPixbufClass parent_class;
70 
71  void (*activate)(RemminaCellRendererPixbuf * renderer);
73 
75 G_GNUC_CONST;
76 
77 G_DEFINE_TYPE(RemminaCellRendererPixbuf, remmina_cell_renderer_pixbuf, GTK_TYPE_CELL_RENDERER_PIXBUF)
78 
79 static guint remmina_cell_renderer_pixbuf_signals[1] =
80 { 0 };
81 
82 static gboolean remmina_cell_renderer_pixbuf_activate(GtkCellRenderer *renderer, GdkEvent *event, GtkWidget *widget,
83  const gchar *path, const GdkRectangle *background_area, const GdkRectangle *cell_area,
84  GtkCellRendererState flags)
85 {
86  TRACE_CALL(__func__);
87  g_signal_emit(G_OBJECT(renderer), remmina_cell_renderer_pixbuf_signals[0], 0, path);
88  return TRUE;
89 }
90 
92 {
93  TRACE_CALL(__func__);
94  GtkCellRendererClass *renderer_class = GTK_CELL_RENDERER_CLASS(klass);
95 
96  renderer_class->activate = remmina_cell_renderer_pixbuf_activate;
97 
98  remmina_cell_renderer_pixbuf_signals[0] = g_signal_new("activate", G_TYPE_FROM_CLASS(klass),
99  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaCellRendererPixbufClass, activate), NULL,
100  NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
101 }
102 
104 {
105  TRACE_CALL(__func__);
106  g_object_set(G_OBJECT(renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
107 }
108 
109 static GtkCellRenderer*
111 {
112  TRACE_CALL(__func__);
113  GtkCellRenderer *renderer;
114 
115  renderer = GTK_CELL_RENDERER(g_object_new(REMMINA_TYPE_CELL_RENDERER_PIXBUF, NULL));
116  return renderer;
117 }
118 
119 /* --------------------- RemminaFTPClient ----------------------------*/
120 G_DEFINE_TYPE( RemminaFTPClient, remmina_ftp_client, GTK_TYPE_GRID)
121 
122 #define BUSY_CURSOR \
123  if (GDK_IS_WINDOW(gtk_widget_get_window(GTK_WIDGET(client)))) \
124  { \
125  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(client)), gdk_cursor_new_for_display(gdk_display_get_default(), GDK_WATCH)); \
126  gdk_display_flush(gdk_display_get_default()); \
127  }
128 
129 #define NORMAL_CURSOR \
130  if (GDK_IS_WINDOW(gtk_widget_get_window(GTK_WIDGET(client)))) \
131  { \
132  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(client)), NULL); \
133  }
134 
135 struct _RemminaFTPClientPriv {
136  GtkWidget *directory_combo;
137  GtkWidget *vpaned;
138 
139  GtkTreeModel *file_list_model;
140  GtkTreeModel *file_list_filter;
141  GtkTreeModel *file_list_sort;
142  GtkWidget *file_list_view;
143  gboolean file_list_show_hidden;
144 
145  GtkTreeModel *task_list_model;
146  GtkWidget *task_list_view;
147 
148  gchar *current_directory;
149  gchar *working_directory;
150 
151  GtkWidget *file_action_widgets[10];
152  gboolean sensitive;
153  gboolean overwrite_all;
154 };
155 
157 
158 enum {
160 };
161 
163 { 0 };
164 
166 {
167  TRACE_CALL(__func__);
168  remmina_ftp_client_signals[OPEN_DIR_SIGNAL] = g_signal_new("open-dir", G_TYPE_FROM_CLASS(klass),
169  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaFTPClientClass, open_dir), NULL, NULL,
170  g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
171  remmina_ftp_client_signals[NEW_TASK_SIGNAL] = g_signal_new("new-task", G_TYPE_FROM_CLASS(klass),
172  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaFTPClientClass, new_task), NULL, NULL,
173  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
174  remmina_ftp_client_signals[CANCEL_TASK_SIGNAL] = g_signal_new("cancel-task", G_TYPE_FROM_CLASS(klass),
175  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaFTPClientClass, cancel_task), NULL, NULL,
176  remmina_marshal_BOOLEAN__INT, G_TYPE_BOOLEAN, 1, G_TYPE_INT);
177  remmina_ftp_client_signals[DELETE_FILE_SIGNAL] = g_signal_new("delete-file", G_TYPE_FROM_CLASS(klass),
178  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaFTPClientClass, delete_file), NULL, NULL,
179  remmina_marshal_BOOLEAN__INT_STRING, G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_STRING);
180 }
181 
182 static void remmina_ftp_client_destroy(RemminaFTPClient *client, gpointer data)
183 {
184  TRACE_CALL(__func__);
186  g_free(priv->current_directory);
187  g_free(priv->working_directory);
188  g_free(priv);
189 }
190 
191 static void remmina_ftp_client_cell_data_filetype_pixbuf(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model,
192  GtkTreeIter *iter, gpointer user_data)
193 {
194  TRACE_CALL(__func__);
195  gint type;
196 
197  /* Same as REMMINA_FTP_TASK_COLUMN_TYPE */
198  gtk_tree_model_get(model, iter, REMMINA_FTP_FILE_COLUMN_TYPE, &type, -1);
199 
200  switch (type) {
202  g_object_set(renderer, "stock-id", "folder", NULL);
203  break;
204  default:
205  g_object_set(renderer, "stock-id", "text-x-generic", NULL);
206  break;
207  }
208 }
209 
210 static void remmina_ftp_client_cell_data_progress_pixbuf(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model,
211  GtkTreeIter *iter, gpointer user_data)
212 {
213  TRACE_CALL(__func__);
214  gint tasktype, status;
215 
216  gtk_tree_model_get(model, iter, REMMINA_FTP_TASK_COLUMN_TASKTYPE, &tasktype, REMMINA_FTP_TASK_COLUMN_STATUS, &status,
217  -1);
218 
219  switch (status) {
221  g_object_set(renderer, "stock-id", "P_ause", NULL);
222  break;
224  g_object_set(renderer, "stock-id",
225  (tasktype == REMMINA_FTP_TASK_TYPE_UPLOAD ? "go-up" : "go-down"), NULL);
226  break;
228  g_object_set(renderer, "stock-id", "_Yes", NULL);
229  break;
231  g_object_set(renderer, "stock-id", "_No", NULL);
232  break;
233  }
234 }
235 
236 static gchar*
238 {
239  TRACE_CALL(__func__);
240 
241  return g_format_size_full((guint64)size, G_FORMAT_SIZE_IEC_UNITS);
242 }
243 
244 static void remmina_ftp_client_cell_data_size(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model,
245  GtkTreeIter *iter, gpointer user_data)
246 {
247  TRACE_CALL(__func__);
248  gfloat size;
249  gchar *str;
250 
251  gtk_tree_model_get(model, iter, REMMINA_FTP_FILE_COLUMN_SIZE, &size, -1);
252 
253  str = remmina_ftp_client_size_to_str(size);
254  g_object_set(renderer, "text", str, NULL);
255  g_object_set(renderer, "xalign", 1.0, NULL);
256  g_free(str);
257 }
258 
259 static void remmina_ftp_client_cell_data_permission(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model,
260  GtkTreeIter *iter, gpointer user_data)
261 {
262  TRACE_CALL(__func__);
263  gint permission;
264  gchar buf[11];
265 
266  gtk_tree_model_get(model, iter, REMMINA_FTP_FILE_COLUMN_PERMISSION, &permission, -1);
267 
268  buf[0] = ((permission & 040000) ? 'd' : '-');
269  buf[1] = ((permission & 0400) ? 'r' : '-');
270  buf[2] = ((permission & 0200) ? 'w' : '-');
271  buf[3] = ((permission & 0100) ? 'x' : '-');
272  buf[4] = ((permission & 040) ? 'r' : '-');
273  buf[5] = ((permission & 020) ? 'w' : '-');
274  buf[6] = ((permission & 010) ? 'x' : '-');
275  buf[7] = ((permission & 04) ? 'r' : '-');
276  buf[8] = ((permission & 02) ? 'w' : '-');
277  buf[9] = ((permission & 01) ? 'x' : '-');
278  buf[10] = '\0';
279 
280  g_object_set(renderer, "text", buf, NULL);
281 }
282 
283 static void remmina_ftp_client_cell_data_size_progress(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model,
284  GtkTreeIter *iter, gpointer user_data)
285 {
286  TRACE_CALL(__func__);
287  gint status;
288  gfloat size, donesize;
289  gchar *strsize, *strdonesize, *str;
290 
291  gtk_tree_model_get(model, iter, REMMINA_FTP_TASK_COLUMN_STATUS, &status, REMMINA_FTP_TASK_COLUMN_SIZE, &size,
292  REMMINA_FTP_TASK_COLUMN_DONESIZE, &donesize, -1);
293 
294  if (status == REMMINA_FTP_TASK_STATUS_FINISH) {
295  str = remmina_ftp_client_size_to_str(size);
296  }else {
297  strsize = remmina_ftp_client_size_to_str(size);
298  strdonesize = remmina_ftp_client_size_to_str(donesize);
299  str = g_strdup_printf("%s / %s", strdonesize, strsize);
300  g_free(strsize);
301  g_free(strdonesize);
302  }
303 
304  g_object_set(renderer, "text", str, NULL);
305  g_object_set(renderer, "xalign", 1.0, NULL);
306  g_free(str);
307 }
308 
309 static void remmina_ftp_client_cell_data_progress(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model,
310  GtkTreeIter *iter, gpointer user_data)
311 {
312  TRACE_CALL(__func__);
313  gint status;
314  gfloat size, donesize;
315  gint progress;
316 
317  gtk_tree_model_get(model, iter, REMMINA_FTP_TASK_COLUMN_STATUS, &status, REMMINA_FTP_TASK_COLUMN_SIZE, &size,
318  REMMINA_FTP_TASK_COLUMN_DONESIZE, &donesize, -1);
319  if (status == REMMINA_FTP_TASK_STATUS_FINISH) {
320  progress = 100;
321  }else {
322  if (size <= 1) {
323  progress = 0;
324  }else {
325  progress = (gint)(donesize / size * 100);
326  if (progress > 99)
327  progress = 99;
328  }
329  }
330  g_object_set(renderer, "value", progress, NULL);
331 }
332 
333 static void remmina_ftp_client_open_dir(RemminaFTPClient *client, const gchar *dir)
334 {
335  TRACE_CALL(__func__);
336  BUSY_CURSOR
337  g_signal_emit(G_OBJECT(client), remmina_ftp_client_signals[OPEN_DIR_SIGNAL], 0, dir);
338  NORMAL_CURSOR
339 }
340 
341 static void remmina_ftp_client_dir_on_activate(GtkWidget *widget, RemminaFTPClient *client)
342 {
343  TRACE_CALL(__func__);
344  remmina_ftp_client_open_dir(client, gtk_entry_get_text(GTK_ENTRY(widget)));
345 }
346 
347 static void remmina_ftp_client_dir_on_changed(GtkWidget *widget, RemminaFTPClient *client)
348 {
349  TRACE_CALL(__func__);
350  GtkWidget *entry = gtk_bin_get_child(GTK_BIN(widget));
351 
352  if (!gtk_widget_is_focus(entry)) {
353  gtk_widget_grab_focus(entry);
354  /* If the text was changed but the entry is not the focus, it should be changed by the drop-down list.
355  Not sure this will always work in the future, but it works right now :) */
356  remmina_ftp_client_open_dir(client, gtk_entry_get_text(GTK_ENTRY(entry)));
357  }
358 }
359 
360 static void remmina_ftp_client_set_file_action_sensitive(RemminaFTPClient *client, gboolean sensitive)
361 {
362  TRACE_CALL(__func__);
363  gint i;
364  for (i = 0; client->priv->file_action_widgets[i]; i++) {
365  gtk_widget_set_sensitive(client->priv->file_action_widgets[i], sensitive);
366  }
367  client->priv->sensitive = sensitive;
368 }
369 
370 static void remmina_ftp_client_file_selection_on_changed(GtkTreeSelection *selection, RemminaFTPClient *client)
371 {
372  TRACE_CALL(__func__);
373  GList *list;
374 
375  list = gtk_tree_selection_get_selected_rows(selection, NULL);
376  remmina_ftp_client_set_file_action_sensitive(client, (list ? TRUE : FALSE));
377  g_list_free(list);
378 }
379 
380 static gchar*
382 {
383  TRACE_CALL(__func__);
385  GtkWidget *dialog;
386  gchar *localdir = NULL;
387 
388  dialog = gtk_file_chooser_dialog_new(_("Choose download location"),
389  GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
390  "_Cancel", GTK_RESPONSE_CANCEL, "_OK", GTK_RESPONSE_ACCEPT, NULL);
391  if (priv->working_directory) {
392  gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), priv->working_directory);
393  }
394  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
395  g_free(priv->working_directory);
396  priv->working_directory = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
397  localdir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
398  }
399  gtk_widget_destroy(dialog);
400  return localdir;
401 }
402 
403 static void remmina_ftp_client_download(RemminaFTPClient *client, GtkTreeIter *piter, const gchar *localdir)
404 {
405  TRACE_CALL(__func__);
407  GtkListStore *store = GTK_LIST_STORE(priv->task_list_model);
408  GtkTreeIter iter;
409  gint type;
410  gchar *name;
411  gfloat size;
412 
413  gtk_tree_model_get(priv->file_list_sort, piter, REMMINA_FTP_FILE_COLUMN_TYPE, &type, REMMINA_FTP_FILE_COLUMN_NAME,
414  &name, REMMINA_FTP_FILE_COLUMN_SIZE, &size, -1);
415 
416  gtk_list_store_append(store, &iter);
417  gtk_list_store_set(store, &iter, REMMINA_FTP_TASK_COLUMN_TYPE, type, REMMINA_FTP_TASK_COLUMN_NAME, name,
418  REMMINA_FTP_TASK_COLUMN_SIZE, size, REMMINA_FTP_TASK_COLUMN_TASKID, remmina_ftp_client_taskid++,
420  priv->current_directory, REMMINA_FTP_TASK_COLUMN_LOCALDIR, localdir, REMMINA_FTP_TASK_COLUMN_STATUS,
422  NULL, -1);
423 
424  g_free(name);
425 
426  g_signal_emit(G_OBJECT(client), remmina_ftp_client_signals[NEW_TASK_SIGNAL], 0);
427 }
428 
429 static gboolean remmina_ftp_client_task_list_on_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_tip,
430  GtkTooltip *tooltip, RemminaFTPClient *client)
431 {
432  TRACE_CALL(__func__);
434  GtkTreeIter iter;
435  GtkTreePath *path = NULL;
436  gchar *tmp;
437 
438  if (!gtk_tree_view_get_tooltip_context(GTK_TREE_VIEW(priv->task_list_view), &x, &y, keyboard_tip, NULL, &path, &iter)) {
439  return FALSE;
440  }
441 
442  gtk_tree_model_get(priv->task_list_model, &iter, REMMINA_FTP_TASK_COLUMN_TOOLTIP, &tmp, -1);
443  if (!tmp)
444  return FALSE;
445 
446  gtk_tooltip_set_text(tooltip, tmp);
447 
448  gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(priv->task_list_view), tooltip, path);
449 
450  gtk_tree_path_free(path);
451  g_free(tmp);
452 
453  return TRUE;
454 }
455 
456 static void remmina_ftp_client_action_parent(GObject *object, RemminaFTPClient *client)
457 {
458  TRACE_CALL(__func__);
459  remmina_ftp_client_open_dir(client, "..");
460 }
461 
462 static void remmina_ftp_client_action_home(GObject *object, RemminaFTPClient *client)
463 {
464  TRACE_CALL(__func__);
465  remmina_ftp_client_open_dir(client, NULL);
466 }
467 
468 static void remmina_ftp_client_action_refresh(GObject *object, RemminaFTPClient *client)
469 {
470  TRACE_CALL(__func__);
471  remmina_ftp_client_open_dir(client, ".");
472 }
473 
474 static void remmina_ftp_client_action_download(GObject *object, RemminaFTPClient *client)
475 {
476  TRACE_CALL(__func__);
478  GtkTreeSelection *selection;
479  gchar *localdir;
480  GList *list, *list_iter;
481  GtkTreeIter iter;
482 
483  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->file_list_view));
484  if (!selection)
485  return;
486  list = gtk_tree_selection_get_selected_rows(selection, NULL);
487  if (!list)
488  return;
489 
490  localdir = remmina_ftp_client_get_download_dir(client);
491  if (!localdir) {
492  g_list_free(list);
493  return;
494  }
495 
496  list_iter = g_list_first(list);
497  while (list_iter) {
498  gtk_tree_model_get_iter(priv->file_list_sort, &iter, (GtkTreePath*)list_iter->data);
499  remmina_ftp_client_download(client, &iter, localdir);
500  list_iter = g_list_next(list_iter);
501  }
502  g_list_free(list);
503  g_free(localdir);
504 }
505 
506 static void remmina_ftp_client_action_delete(GObject *object, RemminaFTPClient *client)
507 {
508  TRACE_CALL(__func__);
510  GtkWidget *dialog;
511  GtkTreeSelection *selection;
512  GList *list, *list_iter;
513  GtkTreeIter iter;
514  gint type;
515  gchar *name;
516  gchar *path;
517  gint response;
518  gboolean ret = TRUE;
519 
520  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->file_list_view));
521  if (!selection)
522  return;
523  list = gtk_tree_selection_get_selected_rows(selection, NULL);
524  if (!list)
525  return;
526 
527  dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))), GTK_DIALOG_MODAL,
528  GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("Are you sure to delete the selected files on server?"));
529  response = gtk_dialog_run(GTK_DIALOG(dialog));
530  gtk_widget_destroy(dialog);
531  if (response != GTK_RESPONSE_YES)
532  return;
533 
534  BUSY_CURSOR
535 
536  list_iter = g_list_first(list);
537  while (list_iter) {
538  gtk_tree_model_get_iter(priv->file_list_sort, &iter, (GtkTreePath*)list_iter->data);
539 
540  gtk_tree_model_get(priv->file_list_sort, &iter, REMMINA_FTP_FILE_COLUMN_TYPE, &type,
541  REMMINA_FTP_FILE_COLUMN_NAME, &name, -1);
542 
543  path = remmina_public_combine_path(priv->current_directory, name);
544  g_signal_emit(G_OBJECT(client), remmina_ftp_client_signals[DELETE_FILE_SIGNAL], 0, type, path, &ret);
545  g_free(name);
546  g_free(path);
547  if (!ret)
548  break;
549 
550  list_iter = g_list_next(list_iter);
551  }
552  g_list_free(list);
553 
554  NORMAL_CURSOR
555 
556  if (ret) {
557  remmina_ftp_client_action_refresh(object, client);
558  }
559 }
560 
561 static void remmina_ftp_client_upload_folder_on_toggled(GtkToggleButton *togglebutton, GtkWidget *widget)
562 {
563  TRACE_CALL(__func__);
564  gtk_file_chooser_set_action(
565  GTK_FILE_CHOOSER(widget),
566  gtk_toggle_button_get_active(togglebutton) ?
567  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : GTK_FILE_CHOOSER_ACTION_OPEN);
568 }
569 
570 static void remmina_ftp_client_action_upload(GObject *object, RemminaFTPClient *client)
571 {
572  TRACE_CALL(__func__);
574  GtkListStore *store = GTK_LIST_STORE(priv->task_list_model);
575  GtkTreeIter iter;
576  GtkWidget *dialog;
577  GtkWidget *upload_folder_check;
578  gint type;
579  GSList *files = NULL;
580  GSList *element;
581  gchar *path;
582  gchar *dir, *name;
583  struct stat st;
584 
585  dialog = gtk_file_chooser_dialog_new(_("Choose a file to upload"),
586  GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(client))), GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel",
587  GTK_RESPONSE_CANCEL, "_OK", GTK_RESPONSE_ACCEPT, NULL);
588  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
589  if (priv->working_directory) {
590  gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), priv->working_directory);
591  }
592  upload_folder_check = gtk_check_button_new_with_label(_("Upload folder"));
593  gtk_widget_show(upload_folder_check);
594  g_signal_connect(G_OBJECT(upload_folder_check), "toggled", G_CALLBACK(remmina_ftp_client_upload_folder_on_toggled),
595  dialog);
596  gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), upload_folder_check);
597  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
598  g_free(priv->working_directory);
599  priv->working_directory = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
600  files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
601  }
602  type = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(upload_folder_check)) ?
604  gtk_widget_destroy(dialog);
605  if (!files)
606  return;
607 
608  for (element = files; element; element = element->next) {
609  path = (gchar*)element->data;
610 
611  if (g_stat(path, &st) < 0)
612  continue;
613 
614  name = g_strrstr(path, "/");
615  if (name) {
616  *name++ = '\0';
617  dir = path;
618  }else {
619  name = path;
620  dir = NULL;
621  }
622 
623  gtk_list_store_append(store, &iter);
624  gtk_list_store_set(store, &iter, REMMINA_FTP_TASK_COLUMN_TYPE, type, REMMINA_FTP_TASK_COLUMN_NAME, name,
630 
631  g_free(path);
632  }
633 
634  g_slist_free(files);
635 
636  g_signal_emit(G_OBJECT(client), remmina_ftp_client_signals[NEW_TASK_SIGNAL], 0);
637 }
638 
639 static void remmina_ftp_client_popup_menu(RemminaFTPClient *client, GdkEventButton *event)
640 {
641  TRACE_CALL(__func__);
642  GtkWidget *menu;
643  GtkWidget *menuitem;
644  GtkWidget *image;
645 
646  menu = gtk_menu_new();
647 
648  menuitem = gtk_menu_item_new_with_label(_("Download"));
649  gtk_widget_show(menuitem);
650  image = gtk_image_new_from_icon_name("remmina-document-save-symbolic", GTK_ICON_SIZE_MENU);
651  gtk_widget_show(image);
652  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
653  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_ftp_client_action_download), client);
654 
655  menuitem = gtk_menu_item_new_with_label(_("Upload"));
656  gtk_widget_show(menuitem);
657  image = gtk_image_new_from_icon_name("remmina-document-send-symbolic", GTK_ICON_SIZE_MENU);
658  gtk_widget_show(image);
659  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
660  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_ftp_client_action_upload), client);
661 
662  menuitem = gtk_menu_item_new_with_mnemonic(_("_Delete"));
663  gtk_widget_show(menuitem);
664  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
665  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_ftp_client_action_delete), client);
666 
667 #if GTK_CHECK_VERSION(3, 22, 0)
668  gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent*)event);
669 #else
670  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
671 #endif
672 }
673 
674 static gboolean remmina_ftp_client_file_list_on_button_press(GtkWidget *widget, GdkEventButton *event, RemminaFTPClient *client)
675 {
676  TRACE_CALL(__func__);
678  GList *list;
679  GtkTreeIter iter;
680  gint type;
681  gchar *name;
682  gchar *localdir;
683 
684  if (event->button == 3) {
685  remmina_ftp_client_popup_menu(client, event);
686  }else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
687  list = gtk_tree_selection_get_selected_rows(
688  gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->file_list_view)), NULL);
689  if (list) {
690  gtk_tree_model_get_iter(priv->file_list_sort, &iter, (GtkTreePath*)list->data);
691  gtk_tree_model_get(priv->file_list_sort, &iter, REMMINA_FTP_FILE_COLUMN_TYPE, &type,
692  REMMINA_FTP_FILE_COLUMN_NAME, &name, -1);
693  switch (type) {
695  remmina_ftp_client_open_dir(client, name);
696  break;
698  default:
699  localdir = remmina_ftp_client_get_download_dir(client);
700  if (localdir) {
701  remmina_ftp_client_download(client, &iter, localdir);
702  g_free(localdir);
703  }
704  break;
705  }
706  g_list_free(list);
707  g_free(name);
708  }
709  }
710 
711  return FALSE;
712 }
713 
714 static void remmina_ftp_client_task_list_cell_on_activate(GtkCellRenderer *renderer, gchar *path, RemminaFTPClient *client)
715 {
716  TRACE_CALL(__func__);
718  GtkTreeIter iter;
719  GtkTreePath *treepath;
720  gint taskid;
721  gboolean ret = FALSE;
722 
723  treepath = gtk_tree_path_new_from_string(path);
724  gtk_tree_model_get_iter(priv->task_list_model, &iter, treepath);
725  gtk_tree_path_free(treepath);
726 
727  gtk_tree_model_get(priv->task_list_model, &iter, REMMINA_FTP_TASK_COLUMN_TASKID, &taskid, -1);
728 
729  g_signal_emit(G_OBJECT(client), remmina_ftp_client_signals[CANCEL_TASK_SIGNAL], 0, taskid, &ret);
730 
731  if (ret) {
732  gtk_list_store_remove(GTK_LIST_STORE(priv->task_list_model), &iter);
733  }
734 }
735 
737 {
738  TRACE_CALL(__func__);
739  GtkWidget *box;
740  GtkWidget *button;
741  GtkWidget *image;
742  gint i = 0;
743 
744  box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
745  gtk_widget_show(box);
746  gtk_button_box_set_layout(GTK_BUTTON_BOX(box), GTK_BUTTONBOX_START);
747  gtk_grid_attach(GTK_GRID(client), box, 0, 0, 1, 1);
748 
749  button = gtk_button_new_with_label(_("Home"));
750  gtk_widget_show(button);
751  gtk_widget_set_tooltip_text(button, _("Go to home folder"));
752  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
753  gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
754  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(remmina_ftp_client_action_home), client);
755 
756  button = gtk_button_new_with_label(_("Up"));
757  gtk_widget_show(button);
758  gtk_widget_set_tooltip_text(button, _("Go to parent folder"));
759  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
760  gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
761  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(remmina_ftp_client_action_parent), client);
762 
763  button = gtk_button_new_with_label(_("Refresh"));
764  gtk_widget_show(button);
765  gtk_widget_set_tooltip_text(button, _("Refresh current folder"));
766  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
767  gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
768  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(remmina_ftp_client_action_refresh), client);
769 
770  button = gtk_button_new_with_label(_("Download"));
771  gtk_widget_show(button);
772  gtk_widget_set_tooltip_text(button, _("Download from server"));
773  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
774  image = gtk_image_new_from_icon_name("remmina-document-save-symbolic", GTK_ICON_SIZE_BUTTON);
775  gtk_button_set_image(GTK_BUTTON(button), image);
776  gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
777  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(remmina_ftp_client_action_download), client);
778 
779  client->priv->file_action_widgets[i++] = button;
780 
781  button = gtk_button_new_with_label(_("Upload"));
782  gtk_widget_show(button);
783  gtk_widget_set_tooltip_text(button, _("Upload to server"));
784  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
785  image = gtk_image_new_from_icon_name("remmina-document-send-symbolic", GTK_ICON_SIZE_BUTTON);
786  gtk_button_set_image(GTK_BUTTON(button), image);
787  gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
788  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(remmina_ftp_client_action_upload), client);
789 
790  button = gtk_button_new_with_label(_("Delete"));
791  gtk_widget_show(button);
792  gtk_widget_set_tooltip_text(button, _("Delete files on server"));
793  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
794  gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
795  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(remmina_ftp_client_action_delete), client);
796 
797  client->priv->file_action_widgets[i++] = button;
798 
799  return box;
800 }
801 
802 void remmina_ftp_client_set_show_hidden(RemminaFTPClient *client, gboolean show_hidden)
803 {
804  TRACE_CALL(__func__);
805  client->priv->file_list_show_hidden = show_hidden;
806  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(client->priv->file_list_filter));
807 }
808 
809 static gboolean remmina_ftp_client_filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter, RemminaFTPClient *client)
810 {
811  TRACE_CALL(__func__);
812  gchar *name;
813  gboolean result = TRUE;
814 
815  if (client->priv->file_list_show_hidden)
816  return TRUE;
817 
818  gtk_tree_model_get(model, iter, REMMINA_FTP_FILE_COLUMN_NAME, &name, -1);
819  if (name && name[0] == '.') {
820  result = FALSE;
821  }
822  g_free(name);
823  return result;
824 }
825 
826 /* Set the overwrite_all status */
828 {
829  TRACE_CALL(__func__);
830  client->priv->overwrite_all = status;
831 }
832 
833 /* Get the overwrite_all status */
835 {
836  TRACE_CALL(__func__);
837  return client->priv->overwrite_all;
838 }
839 
841 {
842  TRACE_CALL(__func__);
843  RemminaFTPClientPriv *priv;
844  GtkWidget *vpaned;
845  GtkWidget *toolbar;
846  GtkWidget *scrolledwindow;
847  GtkWidget *widget;
848  GtkCellRenderer *renderer;
849  GtkTreeViewColumn *column;
850  GtkWidget *vbox;
851 
852  priv = g_new0(RemminaFTPClientPriv, 1);
853  client->priv = priv;
854 
855  /* Initialize overwrite status to FALSE */
856  client->priv->overwrite_all = FALSE;
857 
858  /* Main container */
859  gtk_widget_set_vexpand(GTK_WIDGET(client), TRUE);
860  gtk_widget_set_hexpand(GTK_WIDGET(client), TRUE);
861 
862  /* Toolbar */
863  toolbar = remmina_ftp_client_create_toolbar(client);
864 
865  /* The Paned to separate File List and Task List */
866  vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
867  gtk_widget_set_vexpand(GTK_WIDGET(vpaned), TRUE);
868  gtk_widget_set_hexpand(GTK_WIDGET(vpaned), TRUE);
869  gtk_widget_show(vpaned);
870  gtk_grid_attach_next_to(GTK_GRID(client), vpaned, toolbar, GTK_POS_BOTTOM, 1, 1);
871 
872  priv->vpaned = vpaned;
873 
874  /* Remote */
875  vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
876  gtk_widget_show(vbox);
877  gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, FALSE);
878 
879  /* Remote Directory */
880  widget = gtk_combo_box_text_new_with_entry();
881  gtk_widget_show(widget);
882  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), "/");
883  gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
884 
885  priv->directory_combo = widget;
886 
887  /* Remote File List */
888  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
889  gtk_widget_show(scrolledwindow);
890  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
891  gtk_box_pack_start(GTK_BOX(vbox), scrolledwindow, TRUE, TRUE, 0);
892 
893  widget = gtk_tree_view_new();
894  gtk_widget_show(widget);
895  gtk_container_add(GTK_CONTAINER(scrolledwindow), widget);
896 
897  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), GTK_SELECTION_MULTIPLE);
898 
899  priv->file_list_view = widget;
900 
901  /* Remote File List - Columns */
902  column = gtk_tree_view_column_new();
903  gtk_tree_view_column_set_title(column, _("Filename"));
904  gtk_tree_view_column_set_expand(column, TRUE);
905  gtk_tree_view_column_set_resizable(column, TRUE);
906  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_FILE_COLUMN_NAME_SORT);
907  renderer = gtk_cell_renderer_pixbuf_new();
908  gtk_tree_view_column_pack_start(column, renderer, FALSE);
909  gtk_tree_view_column_set_cell_data_func(column, renderer, remmina_ftp_client_cell_data_filetype_pixbuf, NULL, NULL);
910  renderer = gtk_cell_renderer_text_new();
911  gtk_tree_view_column_pack_start(column, renderer, FALSE);
912  gtk_tree_view_column_add_attribute(column, renderer, "text", REMMINA_FTP_FILE_COLUMN_NAME);
913  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->file_list_view), column);
914 
915  renderer = gtk_cell_renderer_text_new();
916  column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer, NULL);
917  gtk_tree_view_column_set_alignment(column, 1.0);
918  gtk_tree_view_column_set_resizable(column, TRUE);
919  gtk_tree_view_column_set_cell_data_func(column, renderer, remmina_ftp_client_cell_data_size, NULL, NULL);
920  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_FILE_COLUMN_SIZE);
921  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->file_list_view), column);
922 
923  renderer = gtk_cell_renderer_text_new();
924  column = gtk_tree_view_column_new_with_attributes(_("User"), renderer, "text", REMMINA_FTP_FILE_COLUMN_USER, NULL);
925  gtk_tree_view_column_set_resizable(column, TRUE);
926  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_FILE_COLUMN_USER);
927  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->file_list_view), column);
928 
929  renderer = gtk_cell_renderer_text_new();
930  column = gtk_tree_view_column_new_with_attributes(_("Group"), renderer, "text", REMMINA_FTP_FILE_COLUMN_GROUP, NULL);
931  gtk_tree_view_column_set_resizable(column, TRUE);
932  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_FILE_COLUMN_GROUP);
933  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->file_list_view), column);
934 
935  renderer = gtk_cell_renderer_text_new();
936  column = gtk_tree_view_column_new_with_attributes(_("Permission"), renderer, "text", REMMINA_FTP_FILE_COLUMN_PERMISSION,
937  NULL);
938  gtk_tree_view_column_set_resizable(column, TRUE);
939  gtk_tree_view_column_set_cell_data_func(column, renderer, remmina_ftp_client_cell_data_permission, NULL, NULL);
940  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_FILE_COLUMN_PERMISSION);
941  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->file_list_view), column);
942 
943  /* Remote File List - Model */
944  priv->file_list_model = GTK_TREE_MODEL(
945  gtk_list_store_new(REMMINA_FTP_FILE_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT, G_TYPE_STRING,
946  G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING));
947 
948  priv->file_list_filter = gtk_tree_model_filter_new(priv->file_list_model, NULL);
949  gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(priv->file_list_filter),
950  (GtkTreeModelFilterVisibleFunc)remmina_ftp_client_filter_visible_func, client, NULL);
951 
952  priv->file_list_sort = gtk_tree_model_sort_new_with_model(priv->file_list_filter);
953  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->file_list_sort), REMMINA_FTP_FILE_COLUMN_NAME_SORT,
954  GTK_SORT_ASCENDING);
955  gtk_tree_view_set_model(GTK_TREE_VIEW(priv->file_list_view), priv->file_list_sort);
956 
957  /* Task List */
958  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
959  gtk_widget_show(scrolledwindow);
960  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
961  gtk_paned_pack2(GTK_PANED(vpaned), scrolledwindow, FALSE, TRUE);
962 
963  widget = gtk_tree_view_new();
964  gtk_widget_show(widget);
965  gtk_container_add(GTK_CONTAINER(scrolledwindow), widget);
966  g_object_set(widget, "has-tooltip", TRUE, NULL);
967 
968  priv->task_list_view = widget;
969 
970  /* Task List - Columns */
971  column = gtk_tree_view_column_new();
972  gtk_tree_view_column_set_title(column, _("Filename"));
973  gtk_tree_view_column_set_resizable(column, TRUE);
974  gtk_tree_view_column_set_expand(column, TRUE);
975  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_TASK_COLUMN_NAME);
976  renderer = gtk_cell_renderer_pixbuf_new();
977  gtk_tree_view_column_pack_start(column, renderer, FALSE);
978  gtk_tree_view_column_set_cell_data_func(column, renderer, remmina_ftp_client_cell_data_progress_pixbuf, NULL, NULL);
979  renderer = gtk_cell_renderer_pixbuf_new();
980  gtk_tree_view_column_pack_start(column, renderer, FALSE);
981  gtk_tree_view_column_set_cell_data_func(column, renderer, remmina_ftp_client_cell_data_filetype_pixbuf, NULL, NULL);
982  renderer = gtk_cell_renderer_text_new();
983  gtk_tree_view_column_pack_start(column, renderer, FALSE);
984  gtk_tree_view_column_add_attribute(column, renderer, "text", REMMINA_FTP_FILE_COLUMN_NAME);
985  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->task_list_view), column);
986 
987  renderer = gtk_cell_renderer_text_new();
988  column = gtk_tree_view_column_new_with_attributes(_("Remote"), renderer, "text", REMMINA_FTP_TASK_COLUMN_REMOTEDIR,
989  NULL);
990  gtk_tree_view_column_set_resizable(column, TRUE);
991  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_TASK_COLUMN_REMOTEDIR);
992  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->task_list_view), column);
993 
994  renderer = gtk_cell_renderer_text_new();
995  column = gtk_tree_view_column_new_with_attributes(_("Local"), renderer, "text", REMMINA_FTP_TASK_COLUMN_LOCALDIR, NULL);
996  gtk_tree_view_column_set_resizable(column, TRUE);
997  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_TASK_COLUMN_LOCALDIR);
998  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->task_list_view), column);
999 
1000  renderer = gtk_cell_renderer_text_new();
1001  column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer, NULL);
1002  gtk_tree_view_column_set_alignment(column, 1.0);
1003  gtk_tree_view_column_set_resizable(column, TRUE);
1004  gtk_tree_view_column_set_cell_data_func(column, renderer, remmina_ftp_client_cell_data_size_progress, NULL, NULL);
1005  gtk_tree_view_column_set_sort_column_id(column, REMMINA_FTP_TASK_COLUMN_SIZE);
1006  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->task_list_view), column);
1007 
1008  renderer = gtk_cell_renderer_progress_new();
1009  column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer, NULL);
1010  gtk_tree_view_column_set_resizable(column, TRUE);
1011  gtk_tree_view_column_set_cell_data_func(column, renderer, remmina_ftp_client_cell_data_progress, NULL, NULL);
1012  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->task_list_view), column);
1013 
1014  renderer = remmina_cell_renderer_pixbuf_new();
1015  column = gtk_tree_view_column_new_with_attributes(NULL, renderer, NULL);
1016  g_object_set(G_OBJECT(renderer), "stock-id", "_Cancel", NULL);
1017  gtk_tree_view_column_set_resizable(column, FALSE);
1018  gtk_tree_view_append_column(GTK_TREE_VIEW(priv->task_list_view), column);
1019 
1020  g_signal_connect(G_OBJECT(renderer), "activate", G_CALLBACK(remmina_ftp_client_task_list_cell_on_activate), client);
1021 
1022  /* Task List - Model */
1023  priv->task_list_model = GTK_TREE_MODEL(
1024  gtk_list_store_new(REMMINA_FTP_TASK_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT, G_TYPE_INT,
1025  G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_STRING));
1026  gtk_tree_view_set_model(GTK_TREE_VIEW(priv->task_list_view), priv->task_list_model);
1027 
1028  /* Setup the internal signals */
1029  g_signal_connect(G_OBJECT(client), "destroy", G_CALLBACK(remmina_ftp_client_destroy), NULL);
1030  g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(priv->directory_combo))), "activate",
1031  G_CALLBACK(remmina_ftp_client_dir_on_activate), client);
1032  g_signal_connect(G_OBJECT(priv->directory_combo), "changed", G_CALLBACK(remmina_ftp_client_dir_on_changed), client);
1033  g_signal_connect(G_OBJECT(priv->file_list_view), "button-press-event",
1034  G_CALLBACK(remmina_ftp_client_file_list_on_button_press), client);
1035  g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->file_list_view))), "changed",
1036  G_CALLBACK(remmina_ftp_client_file_selection_on_changed), client);
1037  g_signal_connect(G_OBJECT(priv->task_list_view), "query-tooltip",
1038  G_CALLBACK(remmina_ftp_client_task_list_on_query_tooltip), client);
1039 }
1040 
1041 GtkWidget*
1043 {
1044  TRACE_CALL(__func__);
1045  RemminaFTPClient *client;
1046 
1047  client = REMMINA_FTP_CLIENT(g_object_new(REMMINA_TYPE_FTP_CLIENT, NULL));
1048 
1049  return GTK_WIDGET(client);
1050 }
1051 
1053 {
1054  TRACE_CALL(__func__);
1055  gint pos;
1056 
1057  pos = gtk_paned_get_position(GTK_PANED(client->priv->vpaned));
1058  remmina_file_set_int(remminafile, "ftp_vpanedpos", pos);
1059 }
1060 
1062 {
1063  TRACE_CALL(__func__);
1064  gint pos;
1065  GtkAllocation a;
1066 
1067  pos = remmina_file_get_int(remminafile, "ftp_vpanedpos", 0);
1068  if (pos) {
1069  gtk_widget_get_allocation(client->priv->vpaned, &a);
1070  if (a.height > 0 && pos > a.height - 60) {
1071  pos = a.height - 60;
1072  }
1073  gtk_paned_set_position(GTK_PANED(client->priv->vpaned), pos);
1074  }
1075 }
1076 
1078 {
1079  TRACE_CALL(__func__);
1081 
1082  gtk_list_store_clear(GTK_LIST_STORE(priv->file_list_model));
1084 }
1085 
1087 {
1088  TRACE_CALL(__func__);
1090  GtkListStore *store = GTK_LIST_STORE(priv->file_list_model);
1091  GtkTreeIter iter;
1092  va_list args;
1093  gint type;
1094  gchar *name;
1095  gchar *ptr;
1096 
1097  va_start(args, client);
1098  gtk_list_store_append(store, &iter);
1099  gtk_list_store_set_valist(store, &iter, args);
1100  va_end(args);
1101 
1102  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1105  -1);
1106 
1107  ptr = g_strdup_printf("%i%s", type, name);
1108  gtk_list_store_set(store, &iter, REMMINA_FTP_FILE_COLUMN_NAME_SORT, ptr, -1);
1109  g_free(ptr);
1110  g_free(name);
1111 }
1112 
1113 void remmina_ftp_client_set_dir(RemminaFTPClient *client, const gchar *dir)
1114 {
1115  TRACE_CALL(__func__);
1117  GtkTreeModel *model;
1118  GtkTreeIter iter;
1119  gboolean ret;
1120  gchar *t;
1121 
1122  if (priv->current_directory && g_strcmp0(priv->current_directory, dir) == 0)
1123  return;
1124 
1125  model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->directory_combo));
1126  for (ret = gtk_tree_model_get_iter_first(model, &iter); ret; ret = gtk_tree_model_iter_next(model, &iter)) {
1127  gtk_tree_model_get(model, &iter, 0, &t, -1);
1128  if (g_strcmp0(t, dir) == 0) {
1129  gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1130  g_free(t);
1131  break;
1132  }
1133  g_free(t);
1134  }
1135 
1136  gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(priv->directory_combo), dir);
1137  gtk_combo_box_set_active(GTK_COMBO_BOX(priv->directory_combo), 0);
1138 
1139  g_free(priv->current_directory);
1140  priv->current_directory = g_strdup(dir);
1141 }
1142 
1143 gchar*
1145 {
1146  TRACE_CALL(__func__);
1148 
1149  return g_strdup(priv->current_directory);
1150 }
1151 
1154 {
1155  TRACE_CALL(__func__);
1157  GtkTreePath *path;
1158  GtkTreeIter iter;
1159  RemminaFTPTask task;
1160 
1162  /* Allow the execution of this function from a non main thread */
1163  RemminaMTExecData *d;
1164  RemminaFTPTask* retval;
1165  d = (RemminaMTExecData*)g_malloc( sizeof(RemminaMTExecData) );
1166  d->func = FUNC_FTP_CLIENT_GET_WAITING_TASK;
1167  d->p.ftp_client_get_waiting_task.client = client;
1169  retval = d->p.ftp_client_get_waiting_task.retval;
1170  g_free(d);
1171  return retval;
1172  }
1173 
1174  if (!gtk_tree_model_get_iter_first(priv->task_list_model, &iter))
1175  return NULL;
1176 
1177  while (1) {
1178  gtk_tree_model_get(priv->task_list_model, &iter, REMMINA_FTP_TASK_COLUMN_TYPE, &task.type,
1183  &task.donesize, REMMINA_FTP_TASK_COLUMN_TOOLTIP, &task.tooltip, -1);
1184  if (task.status == REMMINA_FTP_TASK_STATUS_WAIT) {
1185  path = gtk_tree_model_get_path(priv->task_list_model, &iter);
1186  task.rowref = gtk_tree_row_reference_new(priv->task_list_model, path);
1187  gtk_tree_path_free(path);
1188  return (RemminaFTPTask*)g_memdup(&task, sizeof(RemminaFTPTask));
1189  }
1190  if (!gtk_tree_model_iter_next(priv->task_list_model, &iter))
1191  break;
1192  }
1193 
1194  return NULL;
1195 }
1196 
1198 {
1199  TRACE_CALL(__func__);
1201  GtkListStore *store = GTK_LIST_STORE(priv->task_list_model);
1202  GtkTreePath *path;
1203  GtkTreeIter iter;
1204 
1206  /* Allow the execution of this function from a non main thread */
1207  RemminaMTExecData *d;
1208  d = (RemminaMTExecData*)g_malloc( sizeof(RemminaMTExecData) );
1209  d->func = FUNC_FTP_CLIENT_UPDATE_TASK;
1210  d->p.ftp_client_update_task.client = client;
1211  d->p.ftp_client_update_task.task = task;
1213  g_free(d);
1214  return;
1215  }
1216 
1217 
1218 
1219  path = gtk_tree_row_reference_get_path(task->rowref);
1220  if (path == NULL)
1221  return;
1222  gtk_tree_model_get_iter(priv->task_list_model, &iter, path);
1223  gtk_tree_path_free(path);
1224  gtk_list_store_set(store, &iter, REMMINA_FTP_TASK_COLUMN_SIZE, task->size, REMMINA_FTP_TASK_COLUMN_STATUS, task->status,
1226 }
1227 
1229 {
1230  TRACE_CALL(__func__);
1231  if (task) {
1232  g_free(task->name);
1233  g_free(task->remotedir);
1234  g_free(task->localdir);
1235  g_free(task->tooltip);
1236  g_free(task);
1237  }
1238 }
1239 
static gboolean remmina_ftp_client_file_list_on_button_press(GtkWidget *widget, GdkEventButton *event, RemminaFTPClient *client)
void remmina_ftp_client_load_state(RemminaFTPClient *client, RemminaFile *remminafile)
void remmina_ftp_client_set_overwrite_status(RemminaFTPClient *client, gboolean status)
static gchar * remmina_ftp_client_size_to_str(gfloat size)
GtkCellRendererPixbufClass parent_class
void remmina_ftp_client_set_show_hidden(RemminaFTPClient *client, gboolean show_hidden)
static guint remmina_ftp_client_signals[LAST_SIGNAL]
static void remmina_ftp_client_cell_data_permission(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
static void remmina_ftp_client_action_home(GObject *object, RemminaFTPClient *client)
struct _RemminaFTPClientPriv RemminaFTPClientPriv
static void remmina_ftp_client_popup_menu(RemminaFTPClient *client, GdkEventButton *event)
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
static GtkWidget * remmina_ftp_client_create_toolbar(RemminaFTPClient *client)
GtkCellRendererPixbuf renderer
static gboolean remmina_ftp_client_task_list_on_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, RemminaFTPClient *client)
struct remmina_masterthread_exec_data::@11::@16 ftp_client_update_task
static void remmina_ftp_client_file_selection_on_changed(GtkTreeSelection *selection, RemminaFTPClient *client)
static void remmina_ftp_client_download(RemminaFTPClient *client, GtkTreeIter *piter, const gchar *localdir)
static void remmina_ftp_client_upload_folder_on_toggled(GtkToggleButton *togglebutton, GtkWidget *widget)
enum remmina_masterthread_exec_data::@10 func
static void remmina_ftp_client_class_init(RemminaFTPClientClass *klass)
gchar * remmina_ftp_client_get_dir(RemminaFTPClient *client)
static void remmina_cell_renderer_pixbuf_class_init(RemminaCellRendererPixbufClass *klass)
static void remmina_ftp_client_set_file_action_sensitive(RemminaFTPClient *client, gboolean sensitive)
static void remmina_ftp_client_cell_data_progress(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
RemminaFTPTask * remmina_ftp_client_get_waiting_task(RemminaFTPClient *client)
void remmina_ftp_client_update_task(RemminaFTPClient *client, RemminaFTPTask *task)
RemminaFTPClientPriv * priv
gchar * remmina_public_combine_path(const gchar *path1, const gchar *path2)
static gchar * remmina_ftp_client_get_download_dir(RemminaFTPClient *client)
static void remmina_ftp_client_open_dir(RemminaFTPClient *client, const gchar *dir)
gboolean remmina_ftp_client_get_overwrite_status(RemminaFTPClient *client)
static void remmina_cell_renderer_pixbuf_init(RemminaCellRendererPixbuf *renderer)
struct _RemminaCellRendererPixbuf RemminaCellRendererPixbuf
struct remmina_masterthread_exec_data::@11::@17 ftp_client_get_waiting_task
static void remmina_ftp_client_dir_on_changed(GtkWidget *widget, RemminaFTPClient *client)
static void remmina_ftp_client_action_refresh(GObject *object, RemminaFTPClient *client)
gboolean remmina_masterthread_exec_is_main_thread()
static void remmina_ftp_client_destroy(RemminaFTPClient *client, gpointer data)
union remmina_masterthread_exec_data::@11 p
struct _RemminaCellRendererPixbufClass RemminaCellRendererPixbufClass
void remmina_ftp_task_free(RemminaFTPTask *task)
void remmina_ftp_client_add_file(RemminaFTPClient *client,...)
static void remmina_ftp_client_dir_on_activate(GtkWidget *widget, RemminaFTPClient *client)
void remmina_ftp_client_set_dir(RemminaFTPClient *client, const gchar *dir)
void remmina_file_set_int(RemminaFile *remminafile, const gchar *setting, gint value)
Definition: remmina_file.c:518
static void remmina_ftp_client_cell_data_size_progress(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
static void remmina_ftp_client_cell_data_progress_pixbuf(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
static void remmina_ftp_client_action_upload(GObject *object, RemminaFTPClient *client)
static void remmina_ftp_client_action_parent(GObject *object, RemminaFTPClient *client)
static gint remmina_ftp_client_taskid
static gboolean remmina_cell_renderer_pixbuf_activate(GtkCellRenderer *renderer, GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags)
void remmina_marshal_BOOLEAN__INT_STRING(GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
void remmina_masterthread_exec_and_wait(RemminaMTExecData *d)
GtkWidget * remmina_ftp_client_new(void)
static void remmina_ftp_client_cell_data_filetype_pixbuf(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
GType remmina_cell_renderer_pixbuf_get_type(void)
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:524
static void remmina_ftp_client_init(RemminaFTPClient *client)
static void remmina_ftp_client_task_list_cell_on_activate(GtkCellRenderer *renderer, gchar *path, RemminaFTPClient *client)
void remmina_ftp_client_clear_file_list(RemminaFTPClient *client)
static void remmina_ftp_client_action_download(GObject *object, RemminaFTPClient *client)
G_DEFINE_TYPE(RemminaFTPClient, remmina_ftp_client, GTK_TYPE_GRID)
static void remmina_ftp_client_cell_data_size(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
static void remmina_ftp_client_action_delete(GObject *object, RemminaFTPClient *client)
static GtkCellRenderer * remmina_cell_renderer_pixbuf_new(void)
void remmina_marshal_BOOLEAN__INT(GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
void remmina_ftp_client_save_state(RemminaFTPClient *client, RemminaFile *remminafile)
GtkTreeRowReference * rowref
static gboolean remmina_ftp_client_filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter, RemminaFTPClient *client)