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_main.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 #include "config.h"
38 #include <gio/gio.h>
39 #include <gio/gdesktopappinfo.h>
40 #include <gdk/gdkkeysyms.h>
41 #include <glib/gi18n.h>
42 #include <gtk/gtk.h>
43 
44 #include "remmina.h"
45 #include "remmina_string_array.h"
46 #include "remmina_public.h"
47 #include "remmina_file.h"
48 #include "remmina_file_manager.h"
49 #include "remmina_file_editor.h"
50 #include "rcw.h"
51 #include "remmina_about.h"
52 #include "remmina_pref.h"
53 #include "remmina_pref_dialog.h"
54 #include "remmina_widget_pool.h"
55 #include "remmina_plugin_manager.h"
56 #include "remmina_log.h"
57 #include "remmina_icon.h"
58 #include "remmina_main.h"
59 #include "remmina_exec.h"
60 #include "remmina_mpchange.h"
61 #include "remmina_external_tools.h"
62 #include "remmina_unlock.h"
64 
66 
67 #define RM_GET_OBJECT(object_name) gtk_builder_get_object(remminamain->builder, object_name)
68 
69 char *fun = "main"; /* just setting the initial value of the calling function name, that is set in the remmina_trace */
70 
71 enum {
80 };
81 
82 static
83 const gchar *supported_mime_types[] = {
84  "x-scheme-handler/rdp",
85  "x-scheme-handler/spice",
86  "x-scheme-handler/vnc",
87  "x-scheme-handler/remmina",
88  "application/x-remmina",
89  NULL
90 };
91 
92 static GActionEntry main_actions[] = {
93  { "about", remmina_main_on_action_application_about, NULL, NULL, NULL },
94  { "news", remmina_main_on_action_application_news, NULL, NULL, NULL },
95  { "default", remmina_main_on_action_application_default, NULL, NULL, NULL },
96  { "mpchange", remmina_main_on_action_application_mpchange, NULL, NULL, NULL },
97  { "plugins", remmina_main_on_action_application_plugins, NULL, NULL, NULL },
98  { "preferences", remmina_main_on_action_application_preferences, NULL, NULL, NULL },
99  { "quit", remmina_main_on_action_application_quit, NULL, NULL, NULL },
100  { "connect", remmina_main_on_action_connection_connect, NULL, NULL, NULL },
101  { "copy", remmina_main_on_action_connection_copy, NULL, NULL, NULL },
102  { "delete", remmina_main_on_action_connection_delete, NULL, NULL, NULL },
103  { "edit", remmina_main_on_action_connection_edit, NULL, NULL, NULL },
104  { "exttools", remmina_main_on_action_connection_external_tools, NULL, NULL, NULL },
105  { "new", remmina_main_on_action_connection_new, NULL, NULL, NULL },
106  { "community", remmina_main_on_action_help_community, NULL, NULL, NULL },
107  { "debug", remmina_main_on_action_help_debug, NULL, NULL, NULL },
108  { "donations", remmina_main_on_action_help_donations, NULL, NULL, NULL },
109  { "homepage", remmina_main_on_action_help_homepage, NULL, NULL, NULL },
110  { "wiki", remmina_main_on_action_help_wiki, NULL, NULL, NULL },
111  { "export", remmina_main_on_action_tools_export, NULL, NULL, NULL },
112  { "import", remmina_main_on_action_tools_import, NULL, NULL, NULL },
113  { "expand", remmina_main_on_action_expand, NULL, NULL, NULL },
114  { "collapse", remmina_main_on_action_collapse, NULL, NULL, NULL },
115 };
116 
117 static GtkTargetEntry remmina_drop_types[] =
118 {
119  { "text/uri-list", 0, 1 }
120 };
121 
123 {
124  "RDP", "VNC", "SSH", "NX", "SPICE"
125 };
126 
130 static void remmina_main_save_size(void)
131 {
132  TRACE_CALL(__func__);
133  if ((gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(remminamain->window))) & GDK_WINDOW_STATE_MAXIMIZED) == 0) {
134  gtk_window_get_size(remminamain->window, &remmina_pref.main_width, &remmina_pref.main_height);
135  remmina_pref.main_maximize = FALSE;
136  }else {
138  }
139 }
140 
141 static void remmina_main_save_expanded_group_func(GtkTreeView *tree_view, GtkTreePath *path, gpointer user_data)
142 {
143  TRACE_CALL(__func__);
144  GtkTreeIter iter;
145  gchar *group;
146 
147  gtk_tree_model_get_iter(remminamain->priv->file_model_sort, &iter, path);
148  gtk_tree_model_get(remminamain->priv->file_model_sort, &iter, GROUP_COLUMN, &group, -1);
149  if (group) {
150  remmina_string_array_add(remminamain->priv->expanded_group, group);
151  g_free(group);
152  }
153 }
154 
156 {
157  TRACE_CALL(__func__);
158  if (GTK_IS_TREE_STORE(remminamain->priv->file_model)) {
159  if (remminamain->priv->expanded_group) {
161  }
162  remminamain->priv->expanded_group = remmina_string_array_new();
163  gtk_tree_view_map_expanded_rows(remminamain->tree_files_list,
164  (GtkTreeViewMappingFunc)remmina_main_save_expanded_group_func, NULL);
165  }
166 }
167 
173 {
174  if (!remminamain || !remminamain->window)
175  return;
179 }
180 
184 static gboolean remmina_main_dexit(gpointer data)
185 {
186  TRACE_CALL(__func__);
188  return FALSE;
189 }
190 
191 gboolean remmina_main_on_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
192 {
193  TRACE_CALL(__func__);
195 
196  /* Forget the main window: it has been deleted */
197  remminamain->window = NULL;
198  g_idle_add(remmina_main_dexit, NULL);
199 
200  return FALSE;
201 }
202 
206 static gboolean remmina_main_rmodestroy(gpointer data)
207 {
211  remminamain->priv->expanded_group = NULL;
212  if (remminamain->priv->file_model)
213  g_object_unref(G_OBJECT(remminamain->priv->file_model));
214 
215  g_object_unref(G_OBJECT(remminamain->priv->file_model_filter));
216  g_object_unref(remminamain->builder);
217  g_free(remminamain->priv->selected_filename);
218  g_free(remminamain->priv->selected_name);
219  g_free(remminamain->priv);
220  g_free(remminamain);
221  remminamain = NULL;
222  return FALSE;
223 }
228 {
229  TRACE_CALL(__func__);
230 
231  if (remminamain) {
232  /* Schedule remminamain object destruction after
233  * GTK will finish to destroy the main window */
234  if (remminamain->window)
236  g_idle_add(remmina_main_rmodestroy, NULL);
237  }
238 }
239 
241 {
242  TRACE_CALL(__func__);
243  g_free(remminamain->priv->selected_filename);
244  g_free(remminamain->priv->selected_name);
245  remminamain->priv->selected_filename = NULL;
246  remminamain->priv->selected_name = NULL;
247 }
248 
249 #ifdef SNAP_BUILD
250 
252 {
253  GtkBuilder *dlgbuilder = NULL;
254  GtkWidget *dlg;
255  GtkWindow *parent;
256  int result;
257  static gboolean shown_once = FALSE;
258  gboolean need_snap_interface_connections = FALSE;
259  GtkWidget* dsa;
261 
262  if (shown_once)
263  return;
264  else
265  shown_once = TRUE;
266 
267  g_print("Remmina is compiled as a SNAP package.\n");
268  remmina_secret_plugin = remmina_plugin_manager_get_secret_plugin();
269  if (remmina_secret_plugin == NULL) {
270  g_print(" but we can’t find the secret plugin inside the SNAP.\n");
271  need_snap_interface_connections = TRUE;
272  } else {
273  if (!remmina_secret_plugin->is_service_available()) {
274  g_print(" but we can’t access a secret service. Secret service or SNAP interface connection is missing.\n");
275  need_snap_interface_connections = TRUE;
276  }
277  }
278 
279  if (need_snap_interface_connections && !remmina_pref.prevent_snap_welcome_message) {
280  dlgbuilder = remmina_public_gtk_builder_new_from_file("remmina_snap_info_dialog.glade");
281  dsa = GTK_WIDGET(gtk_builder_get_object(dlgbuilder, "dontshowagain"));
282  if (dlgbuilder) {
283  parent = remmina_main_get_window();
284  dlg = GTK_WIDGET(gtk_builder_get_object(dlgbuilder, "snapwarndlg"));
285  if (parent)
286  gtk_window_set_transient_for(GTK_WINDOW(dlg), parent);
287  gtk_builder_connect_signals(dlgbuilder, NULL);
288  result = gtk_dialog_run(GTK_DIALOG(dlg));
289  if (result == 1) {
290  remmina_pref.prevent_snap_welcome_message = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dsa));
292  }
293  gtk_widget_destroy(dlg);
294  g_object_unref(dlgbuilder);
295  }
296  }
297 
298 }
299 #endif
300 
301 
302 static gboolean remmina_main_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path,
303  gboolean path_currently_selected, gpointer user_data)
304 {
305  TRACE_CALL(__func__);
306  guint context_id;
307  GtkTreeIter iter;
308  gchar buf[1000];
309 
310  if (path_currently_selected)
311  return TRUE;
312 
313  if (!gtk_tree_model_get_iter(model, &iter, path))
314  return TRUE;
315 
317 
318  gtk_tree_model_get(model, &iter, NAME_COLUMN, &remminamain->priv->selected_name, FILENAME_COLUMN,
319  &remminamain->priv->selected_filename, -1);
320 
321  context_id = gtk_statusbar_get_context_id(remminamain->statusbar_main, "status");
322  gtk_statusbar_pop(remminamain->statusbar_main, context_id);
323  if (remminamain->priv->selected_filename) {
324  g_snprintf(buf, sizeof(buf), "%s (%s)", remminamain->priv->selected_name, remminamain->priv->selected_filename);
325  gtk_statusbar_push(remminamain->statusbar_main, context_id, buf);
326  }else {
327  gtk_statusbar_push(remminamain->statusbar_main, context_id, remminamain->priv->selected_name);
328  }
329  return TRUE;
330 }
331 
332 static void remmina_main_load_file_list_callback(RemminaFile *remminafile, gpointer user_data)
333 {
334  TRACE_CALL(__func__);
335  GtkTreeIter iter;
336  GtkListStore *store;
337  store = GTK_LIST_STORE(user_data);
338  gchar* datetime;
339 
340  datetime = remmina_file_get_datetime(remminafile);
341  gtk_list_store_append(store, &iter);
342  gtk_list_store_set(store, &iter,
344  NAME_COLUMN, remmina_file_get_string(remminafile, "name"),
345  GROUP_COLUMN, remmina_file_get_string(remminafile, "group"),
346  SERVER_COLUMN, remmina_file_get_string(remminafile, "server"),
347  PLUGIN_COLUMN, remmina_file_get_string(remminafile, "protocol"),
348  DATE_COLUMN, datetime,
350  -1);
351  g_free(datetime);
352 }
353 
354 static gboolean remmina_main_load_file_tree_traverse(GNode *node, GtkTreeStore *store, GtkTreeIter *parent)
355 {
356  TRACE_CALL(__func__);
357  GtkTreeIter *iter;
358  RemminaGroupData *data;
359  GNode *child;
360 
361  iter = NULL;
362  if (node->data) {
363  data = (RemminaGroupData*)node->data;
364  iter = g_new0(GtkTreeIter, 1);
365  gtk_tree_store_append(store, iter, parent);
366  gtk_tree_store_set(store, iter,
367  PROTOCOL_COLUMN, "folder-symbolic",
368  NAME_COLUMN, data->name,
369  GROUP_COLUMN, data->group,
370  DATE_COLUMN, data->datetime,
371  FILENAME_COLUMN, NULL,
372  -1);
373  }
374  for (child = g_node_first_child(node); child; child = g_node_next_sibling(child)) {
375  remmina_main_load_file_tree_traverse(child, store, iter);
376  }
377  g_free(iter);
378  return FALSE;
379 }
380 
381 static void remmina_main_load_file_tree_group(GtkTreeStore *store)
382 {
383  TRACE_CALL(__func__);
384  GNode *root;
385 
387  remmina_main_load_file_tree_traverse(root, store, NULL);
389 }
390 
391 static void remmina_main_expand_group_traverse(GtkTreeIter *iter)
392 {
393  TRACE_CALL(__func__);
394  GtkTreeModel *tree;
395  gboolean ret;
396  gchar *group, *filename;
397  GtkTreeIter child;
398  GtkTreePath *path;
399 
400  tree = remminamain->priv->file_model_sort;
401  ret = TRUE;
402  while (ret) {
403  gtk_tree_model_get(tree, iter, GROUP_COLUMN, &group, FILENAME_COLUMN, &filename, -1);
404  if (filename == NULL) {
405  if (remmina_string_array_find(remminamain->priv->expanded_group, group) >= 0) {
406  path = gtk_tree_model_get_path(tree, iter);
407  gtk_tree_view_expand_row(remminamain->tree_files_list, path, FALSE);
408  gtk_tree_path_free(path);
409  }
410  if (gtk_tree_model_iter_children(tree, &child, iter)) {
412  }
413  }
414  g_free(group);
415  g_free(filename);
416 
417  ret = gtk_tree_model_iter_next(tree, iter);
418  }
419 }
420 
421 static void remmina_main_expand_group(void)
422 {
423  TRACE_CALL(__func__);
424  GtkTreeIter iter;
425 
426  if (gtk_tree_model_get_iter_first(remminamain->priv->file_model_sort, &iter)) {
428  }
429 }
430 
431 static gboolean remmina_main_load_file_tree_find(GtkTreeModel *tree, GtkTreeIter *iter, const gchar *match_group)
432 {
433  TRACE_CALL(__func__);
434  gboolean ret, match;
435  gchar *group, *filename;
436  GtkTreeIter child;
437 
438  match = FALSE;
439  ret = TRUE;
440  while (ret) {
441  gtk_tree_model_get(tree, iter, GROUP_COLUMN, &group, FILENAME_COLUMN, &filename, -1);
442  match = (filename == NULL && g_strcmp0(group, match_group) == 0);
443  g_free(group);
444  g_free(filename);
445  if (match)
446  break;
447  if (gtk_tree_model_iter_children(tree, &child, iter)) {
448  match = remmina_main_load_file_tree_find(tree, &child, match_group);
449  if (match) {
450  memcpy(iter, &child, sizeof(GtkTreeIter));
451  break;
452  }
453  }
454  ret = gtk_tree_model_iter_next(tree, iter);
455  }
456  return match;
457 }
458 
459 static void remmina_main_load_file_tree_callback(RemminaFile *remminafile, gpointer user_data)
460 {
461  TRACE_CALL(__func__);
462  GtkTreeIter iter, child;
463  GtkTreeStore *store;
464  gboolean found;
465  gchar* datetime;
466 
467  store = GTK_TREE_STORE(user_data);
468 
469  found = FALSE;
470  if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
471  found = remmina_main_load_file_tree_find(GTK_TREE_MODEL(store), &iter,
472  remmina_file_get_string(remminafile, "group"));
473  }
474 
475  datetime = remmina_file_get_datetime(remminafile);
476  gtk_tree_store_append(store, &child, (found ? &iter : NULL));
477  gtk_tree_store_set(store, &child,
479  NAME_COLUMN, remmina_file_get_string(remminafile, "name"),
480  GROUP_COLUMN, remmina_file_get_string(remminafile, "group"),
481  SERVER_COLUMN, remmina_file_get_string(remminafile, "server"),
482  PLUGIN_COLUMN, remmina_file_get_string(remminafile, "protocol"),
483  DATE_COLUMN, datetime,
485  -1);
486  g_free(datetime);
487 }
488 
489 static void remmina_main_file_model_on_sort(GtkTreeSortable *sortable, gpointer user_data)
490 {
491  TRACE_CALL(__func__);
492  gint columnid;
493  GtkSortType order;
494 
495  gtk_tree_sortable_get_sort_column_id(sortable, &columnid, &order);
499 }
500 
501 static gboolean remmina_main_filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
502 {
503  TRACE_CALL(__func__);
504  gchar *text;
505  gchar *protocol, *name, *group, *server, *plugin, *date, *s;
506  gboolean result = TRUE;
507 
508  text = g_ascii_strdown(gtk_entry_get_text(remminamain->entry_quick_connect_server), -1);
509  if (text && text[0]) {
510  gtk_tree_model_get(model, iter,
511  PROTOCOL_COLUMN, &protocol,
512  NAME_COLUMN, &name,
513  GROUP_COLUMN, &group,
514  SERVER_COLUMN, &server,
515  PLUGIN_COLUMN, &plugin,
516  DATE_COLUMN, &date,
517  -1);
518  if (g_strcmp0(protocol, "folder-symbolic") != 0) {
519  s = g_ascii_strdown(name ? name : "", -1);
520  g_free(name);
521  name = s;
522  s = g_ascii_strdown(group ? group : "", -1);
523  g_free(group);
524  group = s;
525  s = g_ascii_strdown(server ? server : "", -1);
526  g_free(server);
527  server = s;
528  s = g_ascii_strdown(plugin ? plugin : "", -1);
529  g_free(plugin);
530  plugin = s;
531  s = g_ascii_strdown(date ? date : "", -1);
532  g_free(date);
533  date = s;
534  result = (strstr(name, text) || strstr(group, text) || strstr(server, text) || strstr(plugin, text) || strstr(date, text));
535  }
536  g_free(protocol);
537  g_free(name);
538  g_free(group);
539  g_free(server);
540  g_free(plugin);
541  g_free(date);
542  }
543  g_free(text);
544  return result;
545 }
546 
547 static void remmina_main_select_file(const gchar *filename)
548 {
549  TRACE_CALL(__func__);
550  GtkTreeIter iter;
551  GtkTreePath *path;
552  gchar *item_filename;
553  gboolean cmp;
554 
555  if (!gtk_tree_model_get_iter_first(remminamain->priv->file_model_sort, &iter))
556  return;
557 
558  while (TRUE) {
559  gtk_tree_model_get(remminamain->priv->file_model_sort, &iter, FILENAME_COLUMN, &item_filename, -1);
560  cmp = g_strcmp0(item_filename, filename);
561  g_free(item_filename);
562  if (cmp == 0) {
563  gtk_tree_selection_select_iter(gtk_tree_view_get_selection(remminamain->tree_files_list),
564  &iter);
565  path = gtk_tree_model_get_path(remminamain->priv->file_model_sort, &iter);
566  gtk_tree_view_scroll_to_cell(remminamain->tree_files_list, path, NULL, TRUE, 0.5, 0.0);
567  gtk_tree_path_free(path);
568  return;
569  }
570  if (!gtk_tree_model_iter_next(remminamain->priv->file_model_sort, &iter))
571  return;
572  }
573 }
574 
576 {
577  TRACE_CALL(__func__);
578  gint items_count;
579  gchar buf[200];
580  guint context_id;
581  gint view_file_mode;
582  char *save_selected_filename;
583  GtkTreeModel *newmodel;
584 
585  save_selected_filename = g_strdup(remminamain->priv->selected_filename);
587 
588  view_file_mode = remmina_pref.view_file_mode;
589  if (remminamain->priv->override_view_file_mode_to_list)
590  view_file_mode = REMMINA_VIEW_FILE_LIST;
591 
592  switch (remmina_pref.view_file_mode) {
594  gtk_toggle_button_set_active(remminamain->view_toggle_button, FALSE);
595  break;
597  default:
598  gtk_toggle_button_set_active(remminamain->view_toggle_button, TRUE);
599  break;
600  }
601 
602  switch (view_file_mode) {
604  /* Create new GtkTreeStore model */
605  newmodel = GTK_TREE_MODEL(gtk_tree_store_new(7, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING));
606  /* Hide the Group column in the tree view mode */
607  gtk_tree_view_column_set_visible(remminamain->column_files_list_group, FALSE);
608  /* Load groups first */
609  remmina_main_load_file_tree_group(GTK_TREE_STORE(newmodel));
610  /* Load files list */
611  items_count = remmina_file_manager_iterate((GFunc)remmina_main_load_file_tree_callback, (gpointer)newmodel);
612  break;
613 
615  default:
616  /* Create new GtkListStore model */
617  newmodel = GTK_TREE_MODEL(gtk_list_store_new(7, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING));
618  /* Show the Group column in the list view mode */
619  gtk_tree_view_column_set_visible(remminamain->column_files_list_group, TRUE);
620  /* Load files list */
621  items_count = remmina_file_manager_iterate((GFunc)remmina_main_load_file_list_callback, (gpointer)newmodel);
622  break;
623  }
624 
625  /* Unset old model */
626  gtk_tree_view_set_model(remminamain->tree_files_list, NULL);
627 
628  /* Destroy the old model and save the new one */
629  remminamain->priv->file_model = newmodel;
630 
631  /* Create a sorted filtered model based on newmodel and apply it to the TreeView */
632  remminamain->priv->file_model_filter = gtk_tree_model_filter_new(remminamain->priv->file_model, NULL);
633  gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(remminamain->priv->file_model_filter),
634  (GtkTreeModelFilterVisibleFunc)remmina_main_filter_visible_func, NULL, NULL);
635  remminamain->priv->file_model_sort = gtk_tree_model_sort_new_with_model(remminamain->priv->file_model_filter);
636  gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE(remminamain->priv->file_model_sort),
639  gtk_tree_view_set_model(remminamain->tree_files_list, remminamain->priv->file_model_sort);
640  g_signal_connect(G_OBJECT(remminamain->priv->file_model_sort), "sort-column-changed",
641  G_CALLBACK(remmina_main_file_model_on_sort), NULL);
643  /* Select the file previously selected */
644  if (save_selected_filename) {
645  remmina_main_select_file(save_selected_filename);
646  g_free(save_selected_filename);
647  }
648  /* Show in the status bar the total number of connections found */
649  g_snprintf(buf, sizeof(buf), ngettext("Total %i item.", "Total %i items.", items_count), items_count);
650  context_id = gtk_statusbar_get_context_id(remminamain->statusbar_main, "status");
651  gtk_statusbar_pop(remminamain->statusbar_main, context_id);
652  gtk_statusbar_push(remminamain->statusbar_main, context_id, buf);
653 }
654 
656 {
657  TRACE_CALL(__func__);
659 }
660 
661 void remmina_main_on_action_connection_connect(GSimpleAction *action, GVariant *param, gpointer data)
662 {
663  TRACE_CALL(__func__);
664 
665  RemminaFile *remminafile;
666 
667  if (!remminamain->priv->selected_filename)
668  return;
669 
670  remminafile = remmina_file_load(remminamain->priv->selected_filename);
671 
672  if (remminafile == NULL)
673  return;
674 
675  remmina_file_touch(remminafile);
677 
678  remmina_file_free(remminafile);
679 }
680 
681 void remmina_main_on_action_connection_external_tools(GSimpleAction *action, GVariant *param, gpointer data)
682 {
683  TRACE_CALL(__func__);
684  if (!remminamain->priv->selected_filename)
685  return;
686 
687  if (remmina_unlock_new(remminamain->window) == 0)
688  return;
689 
690  remmina_external_tools_from_filename(remminamain, remminamain->priv->selected_filename);
691 }
692 
693 static void remmina_main_file_editor_destroy(GtkWidget *widget, gpointer user_data)
694 {
695  TRACE_CALL(__func__);
697 }
698 
699 void remmina_main_on_action_application_mpchange(GSimpleAction *action, GVariant *param, gpointer data)
700 {
701  TRACE_CALL(__func__);
702  RemminaFile *remminafile;
703 
704  const gchar *username;
705  const gchar *domain;
706  const gchar *group;
707 
708  username = domain = group = "";
709 
710  remminafile = NULL;
711  if (remminamain->priv->selected_filename) {
712  remminafile = remmina_file_load(remminamain->priv->selected_filename);
713  if (remminafile != NULL) {
714  username = remmina_file_get_string(remminafile, "username");
715  domain = remmina_file_get_string(remminafile, "domain");
716  group = remmina_file_get_string(remminafile, "group");
717  }
718  }
719 
720  if (remmina_unlock_new(remminamain->window) == 0)
721  return;
722 
723  remmina_mpchange_schedule(TRUE, group, domain, username, "");
724 
725  if (remminafile != NULL)
726  remmina_file_free(remminafile);
727 
728 }
729 
730 void remmina_main_on_action_connection_new(GSimpleAction *action, GVariant *param, gpointer data)
731 {
732  TRACE_CALL(__func__);
733  GtkWidget *widget;
734 
735  if (remmina_unlock_new(remminamain->window) == 0)
736  return;
737 
738  widget = remmina_file_editor_new();
739  g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(remmina_main_file_editor_destroy), remminamain);
740  gtk_window_set_transient_for(GTK_WINDOW(widget), remminamain->window);
741  gtk_widget_show(widget);
743 }
744 
746 {
747  if (gtk_toggle_button_get_active(remminamain->search_toggle)) {
748  gtk_search_bar_set_search_mode(remminamain->search_bar, TRUE);
749  } else {
750  gtk_search_bar_set_search_mode(remminamain->search_bar, FALSE);
751  }
752 }
753 
755 {
756  if (gtk_toggle_button_get_active(remminamain->view_toggle_button)) {
759  gtk_entry_set_text(remminamain->entry_quick_connect_server, "");
762  }
763  } else {
766  gtk_entry_set_text(remminamain->entry_quick_connect_server, "");
769  }
770  }
771 }
772 
773 void remmina_main_on_action_connection_copy(GSimpleAction *action, GVariant *param, gpointer data)
774 {
775  TRACE_CALL(__func__);
776  GtkWidget *widget;
777 
778  if (remmina_unlock_new(remminamain->window) == 0)
779  return;
780 
781  if (!remminamain->priv->selected_filename)
782  return;
783 
784  widget = remmina_file_editor_new_copy(remminamain->priv->selected_filename);
785  if (widget) {
786  g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(remmina_main_file_editor_destroy), remminamain);
787  gtk_window_set_transient_for(GTK_WINDOW(widget), remminamain->window);
788  gtk_widget_show(widget);
789  }
790  /* Select the file previously selected */
791  if (remminamain->priv->selected_filename) {
793  }
794 }
795 
796 void remmina_main_on_action_connection_edit(GSimpleAction *action, GVariant *param, gpointer data)
797 {
798  TRACE_CALL(__func__);
799  GtkWidget *widget;
800 
801  if (remmina_unlock_new(remminamain->window) == 0)
802  return;
803 
804  if (!remminamain->priv->selected_filename)
805  return;
806 
808  if (widget) {
809  gtk_window_set_transient_for(GTK_WINDOW(widget), remminamain->window);
810  gtk_widget_show(widget);
811  }
812 /* Select the file previously selected */
813  if (remminamain->priv->selected_filename) {
815  }
816 }
817 
818 void remmina_main_on_action_connection_delete(GSimpleAction *action, GVariant *param, gpointer data)
819 {
820  TRACE_CALL(__func__);
821  GtkWidget *dialog;
822  gchar *delfilename;
823 
824  if (!remminamain->priv->selected_filename)
825  return;
826 
827  if (remmina_unlock_new(remminamain->window) == 0)
828  return;
829 
830  dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
831  _("Are you sure you want to delete \"%s\"?"), remminamain->priv->selected_name);
832  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) {
833  delfilename = g_strdup(remminamain->priv->selected_filename);
834  remmina_file_delete(delfilename);
835  g_free(delfilename);
838  }
839  gtk_widget_destroy(dialog);
841 }
842 
843 void remmina_main_on_action_application_preferences(GSimpleAction *action, GVariant *param, gpointer data)
844 {
845  TRACE_CALL(__func__);
846  if (remmina_unlock_new(remminamain->window) == 0)
847  return;
848  GtkDialog *dialog = remmina_pref_dialog_new(0, remminamain->window);
849  gtk_dialog_run(dialog);
850  gtk_widget_destroy(GTK_WIDGET(dialog));
851 }
852 
853 void remmina_main_on_action_application_default(GSimpleAction *action, GVariant *param, gpointer data)
854 {
855  TRACE_CALL(__func__);
856  g_autoptr(GError) error = NULL;
857  GDesktopAppInfo *desktop_info;
858  GAppInfo *info = NULL;
859  g_autofree gchar *id = g_strconcat (REMMINA_APP_ID, ".desktop", NULL);
860  int i;
861 
862  desktop_info = g_desktop_app_info_new (id);
863  if (!desktop_info)
864  return;
865 
866  info = G_APP_INFO (desktop_info);
867 
868  for (i = 0; supported_mime_types[i]; i++) {
869  if (!g_app_info_set_as_default_for_type (info, supported_mime_types[i], &error))
870  g_warning ("Failed to set '%s' as the default application for secondary content type '%s': %s",
871  g_app_info_get_name (info), supported_mime_types[i], error->message);
872  else
873  g_debug ("Set '%s' as the default application for '%s'",
874  g_app_info_get_name (info),
876  }
877 }
878 
879 void remmina_main_on_action_application_quit(GSimpleAction *action, GVariant *param, gpointer data)
880 {
881  // Called by quit signal in remmina_main.glade
882  TRACE_CALL(__func__);
883  g_debug ("Quit intercept");
885 }
886 
888 {
891  gtk_entry_set_text(remminamain->entry_quick_connect_server, "");
894  }
895 }
896 
897 static void remmina_main_import_file_list(GSList *files)
898 {
899  TRACE_CALL(__func__);
900  GtkWidget *dlg;
901  GSList *element;
902  gchar *path;
903  RemminaFilePlugin *plugin;
904  GString *err;
905  RemminaFile *remminafile = NULL;
906  gboolean imported;
907 
908  err = g_string_new(NULL);
909  imported = FALSE;
910  for (element = files; element; element = element->next) {
911  path = (gchar*)element->data;
913  if (plugin && (remminafile = plugin->import_func(path)) != NULL && remmina_file_get_string(remminafile, "name")) {
914  remmina_file_generate_filename(remminafile);
915  remmina_file_save(remminafile);
916  imported = TRUE;
917  }else {
918  g_string_append(err, path);
919  g_string_append_c(err, '\n');
920  }
921  if (remminafile) {
922  remmina_file_free(remminafile);
923  remminafile = NULL;
924  }
925  g_free(path);
926  }
927  g_slist_free(files);
928  if (err->len > 0) {
929  // TRANSLATORS: The placeholder %s is an error message
930  dlg = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
931  _("Unable to import:\n%s"), err->str);
932  g_signal_connect(G_OBJECT(dlg), "response", G_CALLBACK(gtk_widget_destroy), NULL);
933  gtk_widget_show(dlg);
934  }
935  g_string_free(err, TRUE);
936  if (imported) {
938  }
939 }
940 
941 static void remmina_main_action_tools_import_on_response(GtkDialog *dialog, gint response_id, gpointer user_data)
942 {
943  TRACE_CALL(__func__);
944  GSList *files;
945 
946  if (response_id == GTK_RESPONSE_ACCEPT) {
947  files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
949  }
950  gtk_widget_destroy(GTK_WIDGET(dialog));
951 }
952 
953 void remmina_main_on_action_tools_import(GSimpleAction *action, GVariant *param, gpointer data)
954 {
955  TRACE_CALL(__func__);
956  GtkWidget *dialog;
957 
958  dialog = gtk_file_chooser_dialog_new(_("Import"), remminamain->window, GTK_FILE_CHOOSER_ACTION_OPEN, "Import",
959  GTK_RESPONSE_ACCEPT, NULL);
960  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
961  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(remmina_main_action_tools_import_on_response), NULL);
962  gtk_widget_show(dialog);
963 }
964 
965 void remmina_main_on_action_tools_export(GSimpleAction *action, GVariant *param, gpointer data)
966 {
967  TRACE_CALL(__func__);
968  RemminaFilePlugin *plugin;
969  RemminaFile *remminafile;
970  GtkWidget *dialog;
971 
972  if (!remminamain->priv->selected_filename)
973  return;
974 
975  remminafile = remmina_file_load(remminamain->priv->selected_filename);
976  if (remminafile == NULL)
977  return;
979  if (plugin) {
980  dialog = gtk_file_chooser_dialog_new(plugin->export_hints, remminamain->window,
981  GTK_FILE_CHOOSER_ACTION_SAVE, _("_Save"), GTK_RESPONSE_ACCEPT, NULL);
982  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
983  plugin->export_func(remminafile, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
984  }
985  gtk_widget_destroy(dialog);
986  }else {
987  dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
988  _("This protocol does not support exporting."));
989  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
990  gtk_widget_show(dialog);
991  }
992  remmina_file_free(remminafile);
993 }
994 
995 void remmina_main_on_action_application_plugins(GSimpleAction *action, GVariant *param, gpointer data)
996 {
997  TRACE_CALL(__func__);
998  remmina_plugin_manager_show(remminamain->window);
999 }
1000 
1001 void remmina_main_on_action_help_homepage(GSimpleAction *action, GVariant *param, gpointer data)
1002 {
1003  TRACE_CALL(__func__);
1004  g_app_info_launch_default_for_uri("https://www.remmina.org", NULL, NULL);
1005 }
1006 
1007 void remmina_main_on_action_help_wiki(GSimpleAction *action, GVariant *param, gpointer data)
1008 {
1009  TRACE_CALL(__func__);
1010  g_app_info_launch_default_for_uri("https://gitlab.com/Remmina/Remmina/wikis/home", NULL, NULL);
1011 }
1012 
1013 void remmina_main_on_action_help_community(GSimpleAction *action, GVariant *param, gpointer data)
1014 {
1015  TRACE_CALL(__func__);
1016  g_app_info_launch_default_for_uri("https://remmina.org/community", NULL, NULL);
1017 }
1018 
1019 void remmina_main_on_action_help_donations(GSimpleAction *action, GVariant *param, gpointer data)
1020 {
1021  TRACE_CALL(__func__);
1022  g_app_info_launch_default_for_uri("https://www.remmina.org/donations", NULL, NULL);
1023 }
1024 
1025 void remmina_main_on_action_help_debug(GSimpleAction *action, GVariant *param, gpointer data)
1026 {
1027  TRACE_CALL(__func__);
1029 }
1030 
1031 void remmina_main_on_action_application_about(GSimpleAction *action, GVariant *param, gpointer data)
1032 {
1033  TRACE_CALL(__func__);
1034  remmina_about_open(remminamain->window);
1035 };
1036 
1037 void remmina_main_on_action_application_news(GSimpleAction *action, GVariant *param, gpointer data)
1038 {
1039  TRACE_CALL(__func__);
1043 };
1044 
1045 static gboolean remmina_main_quickconnect(void)
1046 {
1047  TRACE_CALL(__func__);
1048  RemminaFile* remminafile;
1049  gchar* server;
1050  gchar *qcp;
1051 
1052 
1053  /* Save quick connect protocol if different from the previous one */
1054  qcp = gtk_combo_box_text_get_active_text(remminamain->combo_quick_connect_protocol);
1055  if (qcp && strcmp(qcp, remmina_pref.last_quickconnect_protocol) != 0) {
1057  remmina_pref.last_quickconnect_protocol = g_strdup(qcp);
1059  }
1060 
1061  remminafile = remmina_file_new();
1062  server = g_strdup(gtk_entry_get_text(remminamain->entry_quick_connect_server));
1063 
1064  remmina_file_set_string(remminafile, "sound", "off");
1065  remmina_file_set_string(remminafile, "server", server);
1066  remmina_file_set_string(remminafile, "name", server);
1067  remmina_file_set_string(remminafile, "protocol", qcp);
1068  g_free(server);
1069 
1070  rcw_open_from_file(remminafile);
1071 
1072  return FALSE;
1073 }
1074 
1075 gboolean remmina_main_quickconnect_on_click(GtkWidget *widget, gpointer user_data)
1076 {
1077  TRACE_CALL(__func__);
1078  if (!kioskmode && kioskmode == FALSE)
1079  return remmina_main_quickconnect();
1080  return FALSE;
1081 }
1082 
1083 /* Select all the text inside the quick search box if there is anything */
1084 void remmina_main_quick_search_enter(GtkWidget *widget, gpointer user_data)
1085 {
1086  if (gtk_entry_get_text(remminamain->entry_quick_connect_server))
1087  gtk_editable_select_region(GTK_EDITABLE(remminamain->entry_quick_connect_server), 0, -1);
1088 }
1089 
1090 void remmina_main_on_action_collapse (GSimpleAction *action, GVariant *param, gpointer data)
1091 {
1092  TRACE_CALL(__func__);
1093  gtk_tree_view_collapse_all (remminamain->tree_files_list);
1094 }
1095 
1096 void remmina_main_on_action_expand (GSimpleAction *action, GVariant *param, gpointer data)
1097 {
1098  TRACE_CALL(__func__);
1099  gtk_tree_view_expand_all (remminamain->tree_files_list);
1100 }
1101 
1102 /* Handle double click on a row in the connections list */
1103 void remmina_main_file_list_on_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
1104 {
1105  TRACE_CALL(__func__);
1106 /* If a connection was selected then execute the default action */
1107  if (remminamain->priv->selected_filename) {
1108  switch (remmina_pref.default_action) {
1109  case REMMINA_ACTION_EDIT:
1110  remmina_main_on_action_connection_edit(NULL, NULL, NULL);
1111  break;
1113  default:
1115  break;
1116  }
1117  }
1118 }
1119 
1120 /* Show the popup menu by the right button mouse click */
1121 gboolean remmina_main_file_list_on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1122 {
1123  TRACE_CALL(__func__);
1124  if (event->button == MOUSE_BUTTON_RIGHT) {
1125  if (!kioskmode && kioskmode == FALSE)
1126 #if GTK_CHECK_VERSION(3, 22, 0)
1127  gtk_menu_popup_at_pointer(GTK_MENU(remminamain->menu_popup), (GdkEvent*)event);
1128 #else
1129  gtk_menu_popup(remminamain->menu_popup, NULL, NULL, NULL, NULL, event->button, event->time);
1130 #endif
1131 
1132 
1133  }
1134  return FALSE;
1135 }
1136 
1137 /* Show the popup menu by the menu key */
1138 gboolean remmina_main_file_list_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
1139 {
1140  TRACE_CALL(__func__);
1141  if (event->keyval == GDK_KEY_Menu) {
1142 #if GTK_CHECK_VERSION(3, 22, 0)
1143  gtk_menu_popup_at_widget(GTK_MENU(remminamain->menu_popup), widget,
1144  GDK_GRAVITY_CENTER, GDK_GRAVITY_CENTER,
1145  (GdkEvent*)event);
1146 #else
1147  gtk_menu_popup(remminamain->menu_popup, NULL, NULL, NULL, NULL, 0, event->time);
1148 #endif
1149  }
1150  return FALSE;
1151 }
1152 
1153 void remmina_main_quick_search_on_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data)
1154 {
1155  TRACE_CALL(__func__);
1156  if (icon_pos == GTK_ENTRY_ICON_SECONDARY) {
1157  gtk_entry_set_text(entry, "");
1158  }
1159 }
1160 
1161 void remmina_main_quick_search_on_changed(GtkEditable *editable, gpointer user_data)
1162 {
1163  TRACE_CALL(__func__);
1164  /* If a search text was input then temporary set the file mode to list */
1165  if (gtk_entry_get_text_length(remminamain->entry_quick_connect_server)) {
1166  if (GTK_IS_TREE_STORE(remminamain->priv->file_model)) {
1167  /* File view mode changed, put it to override and reload list */
1168  remminamain->priv->override_view_file_mode_to_list = TRUE;
1170  }
1171  } else {
1172  if (remminamain->priv->override_view_file_mode_to_list) {
1173  /* File view mode changed, put it to default (disable override) and reload list */
1174  remminamain->priv->override_view_file_mode_to_list = FALSE;
1176  }
1177  }
1178  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(remminamain->priv->file_model_filter));
1179 }
1180 
1181 void remmina_main_on_drag_data_received(GtkWidget *widget, GdkDragContext *drag_context, gint x, gint y,
1182  GtkSelectionData *data, guint info, guint time, gpointer user_data)
1183 {
1184  TRACE_CALL(__func__);
1185  gchar **uris;
1186  GSList *files = NULL;
1187  gint i;
1188 
1189  uris = g_uri_list_extract_uris((const gchar*)gtk_selection_data_get_data(data));
1190  for (i = 0; uris[i]; i++) {
1191  if (strncmp(uris[i], "file://", 7) != 0)
1192  continue;
1193  files = g_slist_append(files, g_strdup(uris[i] + 7));
1194  }
1195  g_strfreev(uris);
1197 }
1198 
1199 /* Add a new menuitem to the Tools menu */
1200 static gboolean remmina_main_add_tool_plugin(gchar *name, RemminaPlugin *plugin, gpointer user_data)
1201 {
1202  TRACE_CALL(__func__);
1203  RemminaToolPlugin *tool_plugin = (RemminaToolPlugin*)plugin;
1204  GtkWidget *menuitem = gtk_menu_item_new_with_label(plugin->description);
1205 
1206  gtk_widget_show(menuitem);
1207  gtk_menu_shell_append(GTK_MENU_SHELL(remminamain->menu_popup_full), menuitem);
1208  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(tool_plugin->exec_func), NULL);
1209  return FALSE;
1210 }
1211 
1212 gboolean remmina_main_on_window_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
1213 {
1214  TRACE_CALL(__func__);
1215  return FALSE;
1216 }
1217 
1218 /* Remmina main window initialization */
1219 static void remmina_main_init(void)
1220 {
1221  TRACE_CALL(__func__);
1222  int i, qcp_idx, qcp_actidx;
1223  char *name;
1224 
1225  remmina_debug ("Initializing the Remmina main window");
1227  if (!kioskmode && kioskmode == FALSE) {
1228  gtk_window_set_title(remminamain->window, _("Remmina Remote Desktop Client"));
1229  }else {
1230  gtk_window_set_title(remminamain->window, _("Remmina Kiosk"));
1231  }
1232  if (!kioskmode && kioskmode == FALSE) {
1233  gtk_window_set_default_size(remminamain->window, remmina_pref.main_width, remmina_pref.main_height);
1235  gtk_window_maximize(remminamain->window);
1236  }
1237  }
1238  /* Honor global preferences Search Bar visibility */
1240  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(RM_GET_OBJECT("search_toggle")), FALSE);
1241 
1242  /* Add a GtkMenuItem to the Tools menu for each plugin of type REMMINA_PLUGIN_TYPE_TOOL */
1244 
1245  /* Add available quick connect protocols to remminamain->combo_quick_connect_protocol */
1246  qcp_idx = qcp_actidx = 0;
1247  for (i = 0; i < sizeof(quick_connect_plugin_list) / sizeof(quick_connect_plugin_list[0]); i++) {
1248  name = quick_connect_plugin_list[i];
1250  gtk_combo_box_text_append(remminamain->combo_quick_connect_protocol, name, name);
1252  qcp_actidx = qcp_idx;
1253  qcp_idx ++;
1254  }
1255  }
1256  gtk_combo_box_set_active(GTK_COMBO_BOX(remminamain->combo_quick_connect_protocol), qcp_actidx);
1257 
1258  /* Connect the group accelerators to the GtkWindow */
1259  //gtk_window_add_accel_group(remminamain->window, remminamain->accelgroup_shortcuts);
1260  /* Set the Quick Connection */
1261  gtk_entry_set_activates_default(remminamain->entry_quick_connect_server, TRUE);
1262  /* Set the TreeView for the files list */
1263  gtk_tree_selection_set_select_function(
1264  gtk_tree_view_get_selection(remminamain->tree_files_list),
1265  remmina_main_selection_func, NULL, NULL);
1267  gtk_tree_view_set_search_entry(remminamain->tree_files_list, GTK_ENTRY(remminamain->entry_quick_connect_server));
1268  /* Load the files list */
1270 
1271  /* Drag-n-drop support */
1272  gtk_drag_dest_set(GTK_WIDGET(remminamain->window), GTK_DEST_DEFAULT_ALL, remmina_drop_types, 1, GDK_ACTION_COPY);
1273 
1274  /* Finish initialization */
1275  remminamain->priv->initialized = TRUE;
1276 
1277  /* Register the window in remmina_widget_pool with GType=GTK_WINDOW and TAG=remmina-main-window */
1278  g_object_set_data(G_OBJECT(remminamain->window), "tag", "remmina-main-window");
1279  remmina_widget_pool_register(GTK_WIDGET(remminamain->window));
1280 }
1281 
1282 /* Signal handler for "show" on remminamain->window */
1283 void remmina_main_on_show(GtkWidget *w, gpointer user_data)
1284 {
1285  TRACE_CALL(__func__);
1286 #ifdef SNAP_BUILD
1288 #endif
1289 }
1290 
1291 /* RemminaMain instance */
1292 GtkWidget* remmina_main_new(void)
1293 {
1294  TRACE_CALL(__func__);
1295  GSimpleActionGroup *actions;
1296  GtkAccelGroup *accel_group = NULL;
1297 
1298  remminamain = g_new0(RemminaMain, 1);
1299  remminamain->priv = g_new0(RemminaMainPriv, 1);
1300  /* Assign UI widgets to the private members */
1301  remminamain->builder = remmina_public_gtk_builder_new_from_file("remmina_main.glade");
1302  remminamain->window = GTK_WINDOW(RM_GET_OBJECT("RemminaMain"));
1303  if (kioskmode && kioskmode == TRUE) {
1304  gtk_window_set_position(remminamain->window, GTK_WIN_POS_CENTER_ALWAYS);
1305  gtk_window_set_default_size(remminamain->window, 800, 400);
1306  gtk_window_set_resizable(remminamain->window, FALSE);
1307  }
1308  /* New Button */
1309  remminamain->button_new = GTK_BUTTON(RM_GET_OBJECT("button_new"));
1310  if (kioskmode && kioskmode == TRUE)
1311  gtk_widget_set_sensitive(GTK_WIDGET(remminamain->button_new), FALSE);
1312  /* Search bar */
1313  remminamain->search_toggle = GTK_TOGGLE_BUTTON(RM_GET_OBJECT("search_toggle"));
1314  remminamain->search_bar = GTK_SEARCH_BAR(RM_GET_OBJECT("search_bar"));
1315  /* view mode list/tree */
1316  remminamain->view_toggle_button = GTK_TOGGLE_BUTTON(RM_GET_OBJECT("view_toggle_button"));
1317  if (kioskmode && kioskmode == TRUE)
1318  gtk_widget_set_sensitive(GTK_WIDGET(remminamain->view_toggle_button), FALSE);
1319 
1320  /* Menu widgets */
1321  remminamain->menu_popup = GTK_MENU(RM_GET_OBJECT("menu_popup"));
1322  remminamain->menu_header_button = GTK_MENU_BUTTON(RM_GET_OBJECT("menu_header_button"));
1323  remminamain->menu_popup_full = GTK_MENU(RM_GET_OBJECT("menu_popup_full"));
1324  if (kioskmode && kioskmode == TRUE) {
1325  gtk_widget_set_sensitive(GTK_WIDGET(remminamain->menu_popup_full), FALSE);
1326  gtk_widget_set_sensitive(GTK_WIDGET(remminamain->menu_header_button), FALSE);
1327  }
1328  /* View mode radios */
1329  remminamain->menuitem_view_mode_list = GTK_RADIO_MENU_ITEM(RM_GET_OBJECT("menuitem_view_mode_list"));
1330  remminamain->menuitem_view_mode_tree = GTK_RADIO_MENU_ITEM(RM_GET_OBJECT("menuitem_view_mode_tree"));
1331  /* Quick connect objects */
1332  remminamain->box_quick_connect = GTK_BOX(RM_GET_OBJECT("box_quick_connect"));
1333  remminamain->combo_quick_connect_protocol = GTK_COMBO_BOX_TEXT(RM_GET_OBJECT("combo_quick_connect_protocol"));
1334  if (kioskmode && kioskmode == TRUE)
1335  gtk_widget_set_sensitive(GTK_WIDGET(remminamain->combo_quick_connect_protocol), FALSE);
1336  remminamain->entry_quick_connect_server = GTK_ENTRY(RM_GET_OBJECT("entry_quick_connect_server"));
1337  /* Other widgets */
1338  remminamain->tree_files_list = GTK_TREE_VIEW(RM_GET_OBJECT("tree_files_list"));
1339  remminamain->column_files_list_group = GTK_TREE_VIEW_COLUMN(RM_GET_OBJECT("column_files_list_group"));
1340  remminamain->statusbar_main = GTK_STATUSBAR(RM_GET_OBJECT("statusbar_main"));
1341  /* Non widget objects */
1342  actions = g_simple_action_group_new();
1343  g_action_map_add_action_entries(G_ACTION_MAP(actions), main_actions, G_N_ELEMENTS(main_actions), remminamain->window);
1344  gtk_widget_insert_action_group(GTK_WIDGET(remminamain->window), "main", G_ACTION_GROUP(actions));
1345  g_object_unref(actions);
1346  /* Accelerators */
1347  accel_group = gtk_accel_group_new();
1348  gtk_window_add_accel_group(remminamain->window, accel_group);
1349  gtk_accel_group_connect (accel_group, GDK_KEY_Q, GDK_CONTROL_MASK, 0,
1350  g_cclosure_new_swap (G_CALLBACK (remmina_main_on_action_application_quit), NULL, NULL));
1351  gtk_accel_group_connect (accel_group, GDK_KEY_P, GDK_CONTROL_MASK, 0,
1352  g_cclosure_new_swap (G_CALLBACK (remmina_main_on_action_application_preferences), NULL, NULL));
1353 
1354  /* Connect signals */
1355  gtk_builder_connect_signals(remminamain->builder, NULL);
1356  /* Initialize the window and load the preferences */
1358  return GTK_WIDGET(remminamain->window);
1359 }
1360 
1362 {
1363  if (!remminamain)
1364  return NULL;
1365  if (!remminamain->priv)
1366  return NULL;
1367  if (!remminamain->priv->initialized)
1368  return NULL;
1369  remminamain->window = GTK_WINDOW(RM_GET_OBJECT("RemminaMain"));
1370  return remminamain->window;
1371 }
1372 
1374 {
1375  if (!remminamain)
1376  return;
1378 }
1379 
1380 void remmina_main_show_warning_dialog(const gchar* message)
1381 {
1382  GtkWidget* dialog;
1383 
1384  if (remminamain->window) {
1385  dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
1386  message, g_get_application_name());
1387  gtk_dialog_run(GTK_DIALOG(dialog));
1388  gtk_widget_destroy(dialog);
1389  }
1390 }
G_BEGIN_DECLS struct _RemminaGroupData RemminaGroupData
RemminaFile * remmina_file_load(const gchar *filename)
Definition: remmina_file.c:327
static RemminaSecretPlugin * remmina_secret_plugin
void remmina_log_start(void)
Definition: remmina_log.c:131
static GActionEntry main_actions[]
Definition: remmina_main.c:92
gboolean hide_searchbar
Definition: remmina_pref.h:133
void remmina_file_free(RemminaFile *remminafile)
Definition: remmina_file.c:548
GtkToggleButton * search_toggle
Definition: remmina_main.h:59
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:449
GtkRadioMenuItem * menuitem_view_mode_list
Definition: remmina_main.h:52
static void remmina_main_load_file_tree_callback(RemminaFile *remminafile, gpointer user_data)
Definition: remmina_main.c:459
void remmina_main_on_action_help_homepage(GSimpleAction *action, GVariant *param, gpointer data)
gint default_action
Definition: remmina_pref.h:123
static void remmina_main_show_snap_welcome()
Definition: remmina_main.c:251
GtkWindow * remmina_main_get_window()
gchar * selected_filename
Definition: remmina_main.h:88
GtkTreeModel * file_model
Definition: remmina_main.h:82
static void remmina_main_save_expanded_group(void)
Definition: remmina_main.c:155
static gboolean remmina_main_rmodestroy(gpointer data)
Called after the main window is destroyed, will cleanup remminamain object and related data...
Definition: remmina_main.c:206
gboolean remmina_external_tools_from_filename(RemminaMain *remminamain, gchar *remminafilename)
RemminaMainPriv * priv
Definition: remmina_main.h:78
const gchar * remmina_file_get_filename(RemminaFile *remminafile)
Definition: remmina_file.c:184
void remmina_main_on_action_connection_connect(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:661
void remmina_main_on_search_toggle()
Definition: remmina_main.c:745
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
GtkRadioMenuItem * menuitem_view_mode_tree
Definition: remmina_main.h:53
const gchar * export_hints
Definition: plugin.h:105
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
void remmina_main_load_files_cb()
Definition: remmina_main.c:655
void remmina_widget_pool_register(GtkWidget *widget)
void(* exec_func)(void)
Definition: plugin.h:115
gchar * last_quickconnect_protocol
Definition: remmina_pref.h:189
gboolean remmina_main_file_list_on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static gboolean remmina_main_filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
Definition: remmina_main.c:501
RemminaFilePlugin * remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile)
void remmina_main_on_action_application_news(GSimpleAction *action, GVariant *param, gpointer data)
void remmina_file_manager_free_group_tree(GNode *node)
RemminaFilePlugin * remmina_plugin_manager_get_import_file_handler(const gchar *file)
void remmina_main_on_action_tools_import(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:953
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
GtkBuilder * remmina_public_gtk_builder_new_from_file(gchar *filename)
static void remmina_main_save_size(void)
Save the Remmina Main Window size to assure the main geometry at each restart.
Definition: remmina_main.c:130
void remmina_main_on_action_application_mpchange(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:699
void remmina_main_on_view_toggle()
Definition: remmina_main.c:754
void remmina_icon_populate_menu(void)
Definition: remmina_icon.c:264
gchar * remmina_file_get_datetime(RemminaFile *remminafile)
Return the string date of the last time a file has been modified.
Definition: remmina_file.c:789
static void remmina_main_file_model_on_sort(GtkTreeSortable *sortable, gpointer user_data)
Definition: remmina_main.c:489
GtkWidget * remmina_main_new(void)
void remmina_main_on_action_help_debug(GSimpleAction *action, GVariant *param, gpointer data)
gint remmina_string_array_find(RemminaStringArray *array, const gchar *str)
gboolean(* is_service_available)(void)
Definition: plugin.h:138
gchar * remmina_string_array_to_string(RemminaStringArray *array)
void remmina_file_generate_filename(RemminaFile *remminafile)
Definition: remmina_file.c:108
void remmina_main_save_before_destroy()
Save the Remmina Main Window size and the exapnded group before to close Remmina. ...
Definition: remmina_main.c:172
const gchar * description
Definition: plugin.h:56
static void remmina_main_load_file_list_callback(RemminaFile *remminafile, gpointer user_data)
Definition: remmina_main.c:332
static gboolean remmina_main_load_file_tree_traverse(GNode *node, GtkTreeStore *store, GtkTreeIter *parent)
Definition: remmina_main.c:354
GtkTreeModel * file_model_filter
Definition: remmina_main.h:83
static gboolean remmina_main_load_file_tree_find(GtkTreeModel *tree, GtkTreeIter *iter, const gchar *match_group)
Definition: remmina_main.c:431
gchar * selected_name
Definition: remmina_main.h:89
void remmina_main_on_action_help_wiki(GSimpleAction *action, GVariant *param, gpointer data)
gboolean(* export_func)(RemminaFile *file, const gchar *to_file)
Definition: plugin.h:104
gint main_sort_order
Definition: remmina_pref.h:183
void remmina_main_on_action_application_default(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:853
gboolean remmina_main_on_window_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
void remmina_debug(const gchar *fmt,...)
Print a string in the Remmina Debug Windows and in the terminal.
Definition: remmina_log.c:194
void remmina_main_update_file_datetime(RemminaFile *file)
GtkMenuButton * menu_header_button
Definition: remmina_main.h:50
static void remmina_main_load_file_tree_group(GtkTreeStore *store)
Definition: remmina_main.c:381
void remmina_main_on_action_connection_copy(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:773
void remmina_plugin_manager_show(GtkWindow *parent)
static void remmina_main_import_file_list(GSList *files)
Definition: remmina_main.c:897
void remmina_main_on_action_collapse(GSimpleAction *action, GVariant *param, gpointer data)
GtkMenu * menu_popup
Definition: remmina_main.h:49
void remmina_main_on_action_help_donations(GSimpleAction *action, GVariant *param, gpointer data)
gboolean initialized
Definition: remmina_main.h:86
void remmina_mpchange_schedule(gboolean has_domain, const gchar *group, const gchar *domain, const gchar *username, const gchar *password)
GtkComboBoxText * combo_quick_connect_protocol
Definition: remmina_main.h:65
gboolean prevent_snap_welcome_message
Definition: remmina_pref.h:188
gchar * expanded_group
Definition: remmina_pref.h:184
GtkBox * box_quick_connect
Definition: remmina_main.h:64
void remmina_main_on_drag_data_received(GtkWidget *widget, GdkDragContext *drag_context, gint x, gint y, GtkSelectionData *data, guint info, guint time, gpointer user_data)
void remmina_main_show_warning_dialog(const gchar *message)
void remmina_main_on_action_connection_new(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:730
void remmina_main_quick_search_enter(GtkWidget *widget, gpointer user_data)
void remmina_file_delete(const gchar *filename)
Definition: remmina_file.c:723
GtkButton * button_new
Definition: remmina_main.h:56
GtkMenu * menu_popup_full
Definition: remmina_main.h:51
void remmina_string_array_free(RemminaStringArray *array)
gint main_sort_column_id
Definition: remmina_pref.h:182
gboolean main_maximize
Definition: remmina_pref.h:181
GtkStatusbar * statusbar_main
Definition: remmina_main.h:71
void remmina_main_quick_search_on_changed(GtkEditable *editable, gpointer user_data)
void remmina_main_on_action_application_preferences(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:843
GtkBuilder * builder
Definition: remmina_main.h:46
glong periodic_rmnews_get_count
Definition: remmina_pref.h:207
void remmina_main_on_action_connection_delete(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:818
RemminaStringArray * remmina_string_array_new_from_string(const gchar *strs)
gint view_file_mode
Definition: remmina_pref.h:175
RemminaStringArray * remmina_string_array_new(void)
void remmina_file_touch(RemminaFile *remminafile)
Update the atime and mtime of a given filename.
Definition: remmina_file.c:839
GtkWidget * remmina_file_editor_new_from_filename(const gchar *filename)
static void remmina_main_save_expanded_group_func(GtkTreeView *tree_view, GtkTreePath *path, gpointer user_data)
Definition: remmina_main.c:141
void remmina_main_on_show(GtkWidget *w, gpointer user_data)
static void remmina_main_init(void)
GtkTreeView * tree_files_list
Definition: remmina_main.h:69
static void remmina_main_select_file(const gchar *filename)
Definition: remmina_main.c:547
gboolean remmina_main_on_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
Definition: remmina_main.c:191
void remmina_main_on_action_help_community(GSimpleAction *action, GVariant *param, gpointer data)
GtkToggleButton * view_toggle_button
Definition: remmina_main.h:60
void remmina_main_destroy()
Called when the main window is destroyed via a call from gtk_widget_destroy()
Definition: remmina_main.c:227
RemminaFile * remmina_file_new(void)
Definition: remmina_file.c:90
GtkTreeViewColumn * column_files_list_group
Definition: remmina_main.h:70
void remmina_main_file_list_on_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
static void remmina_main_expand_group_traverse(GtkTreeIter *iter)
Definition: remmina_main.c:391
RemminaStringArray * expanded_group
Definition: remmina_main.h:91
static gboolean remmina_main_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer user_data)
Definition: remmina_main.c:302
void remmina_main_on_action_connection_external_tools(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:681
static void remmina_main_clear_selection_data(void)
Definition: remmina_main.c:240
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:3892
void remmina_main_on_date_column_sort_clicked()
Definition: remmina_main.c:887
RemminaPref remmina_pref
Definition: rcw.c:73
char * fun
Definition: remmina_main.c:69
gboolean remmina_main_file_list_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
void remmina_about_open(GtkWindow *parent)
Definition: remmina_about.c:45
GtkWidget * remmina_file_editor_new(void)
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
static gboolean remmina_main_add_tool_plugin(gchar *name, RemminaPlugin *plugin, gpointer user_data)
gboolean remmina_pref_save(void)
Definition: remmina_pref.c:707
void remmina_main_on_action_connection_edit(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:796
GtkTreeModel * file_model_sort
Definition: remmina_main.h:84
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:3847
static const gchar * supported_mime_types[]
Definition: remmina_main.c:83
void remmina_main_on_action_expand(GSimpleAction *action, GVariant *param, gpointer data)
gboolean kioskmode
Definition: remmina.c:79
GtkWindow * window
Definition: remmina_main.h:47
void remmina_application_condexit(RemminaCondExitType why)
Definition: remmina_exec.c:117
GtkDialog * remmina_pref_dialog_new(gint default_tab, GtkWindow *parent)
void remmina_file_set_string(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: remmina_file.c:423
static char * quick_connect_plugin_list[]
Definition: remmina_main.c:122
void remmina_file_save(RemminaFile *remminafile)
Definition: remmina_file.c:561
static gboolean remmina_main_quickconnect(void)
RemminaFile *(* import_func)(const gchar *from_file)
Definition: plugin.h:102
void remmina_string_array_add(RemminaStringArray *array, const gchar *str)
static GtkTargetEntry remmina_drop_types[]
Definition: remmina_main.c:117
GtkSearchBar * search_bar
Definition: remmina_main.h:62
static RemminaMain * remminamain
Definition: remmina_main.c:65
GtkWidget * remmina_file_editor_new_copy(const gchar *filename)
void remmina_main_on_action_application_quit(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:879
GtkEntry * entry_quick_connect_server
Definition: remmina_main.h:66
gint remmina_unlock_new(GtkWindow *parent)
const gchar * remmina_file_get_icon_name(RemminaFile *remminafile)
Definition: remmina_file.c:697
glong periodic_rmnews_last_get
Definition: remmina_pref.h:206
GNode * remmina_file_manager_get_group_tree(void)
gboolean override_view_file_mode_to_list
Definition: remmina_main.h:90
void remmina_main_on_action_tools_export(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:965
static void remmina_main_file_editor_destroy(GtkWidget *widget, gpointer user_data)
Definition: remmina_main.c:693
gint remmina_file_manager_iterate(GFunc func, gpointer user_data)
static void remmina_main_expand_group(void)
Definition: remmina_main.c:421
void remmina_main_quick_search_on_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data)
static void remmina_main_load_files()
Definition: remmina_main.c:575
void remmina_main_on_action_application_about(GSimpleAction *action, GVariant *param, gpointer data)
static void remmina_main_action_tools_import_on_response(GtkDialog *dialog, gint response_id, gpointer user_data)
Definition: remmina_main.c:941
void remmina_main_on_action_application_plugins(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:995
static gboolean remmina_main_dexit(gpointer data)
Try to exit remmina after a delete window event.
Definition: remmina_main.c:184
gboolean remmina_main_quickconnect_on_click(GtkWidget *widget, gpointer user_data)