summaryrefslogtreecommitdiffstats
path: root/src/remmina_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/remmina_main.c')
-rw-r--r--src/remmina_main.c1844
1 files changed, 1844 insertions, 0 deletions
diff --git a/src/remmina_main.c b/src/remmina_main.c
new file mode 100644
index 0000000..81c78f3
--- /dev/null
+++ b/src/remmina_main.c
@@ -0,0 +1,1844 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2009-2011 Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2022 Antenore Gatta, Giovanni Panozzo
+ * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
+ * Copyright (C) 2023-2024 Hiroyuki Tanaka, Sunil Bhat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ *
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include <gio/gio.h>
+#ifndef __APPLE__
+#include <gio/gdesktopappinfo.h>
+#endif
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "remmina.h"
+#include "remmina_string_array.h"
+#include "remmina_public.h"
+#include "remmina_file.h"
+#include "remmina_file_manager.h"
+#include "remmina_file_editor.h"
+#include "rcw.h"
+#include "remmina_about.h"
+#include "remmina_pref.h"
+#include "remmina_pref_dialog.h"
+#include "remmina_widget_pool.h"
+#include "remmina_plugin_manager.h"
+#include "remmina_bug_report.h"
+#include "remmina_log.h"
+#include "remmina_icon.h"
+#include "remmina_main.h"
+#include "remmina_exec.h"
+#include "remmina_mpchange.h"
+#include "remmina_external_tools.h"
+#include "remmina_unlock.h"
+#include "remmina/remmina_trace_calls.h"
+
+static RemminaMain *remminamain;
+
+#define RM_GET_OBJECT(object_name) gtk_builder_get_object(remminamain->builder, object_name)
+
+enum {
+ PROTOCOL_COLUMN,
+ NAME_COLUMN,
+ GROUP_COLUMN,
+ SERVER_COLUMN,
+ PLUGIN_COLUMN,
+ DATE_COLUMN,
+ FILENAME_COLUMN,
+ LABELS_COLUMN,
+ NOTES_COLUMN,
+ N_COLUMNS
+};
+
+static
+const gchar *supported_mime_types[] = {
+ "x-scheme-handler/rdp",
+ "x-scheme-handler/spice",
+ "x-scheme-handler/vnc",
+ "x-scheme-handler/remmina",
+ "application/x-remmina",
+ NULL
+};
+
+static GActionEntry app_actions[] = {
+ { "about", remmina_main_on_action_application_about, NULL, NULL, NULL },
+ { "default", remmina_main_on_action_application_default, NULL, NULL, NULL },
+ { "mpchange", remmina_main_on_action_application_mpchange, NULL, NULL, NULL },
+ { "plugins", remmina_main_on_action_application_plugins, NULL, NULL, NULL },
+ { "preferences", remmina_main_on_action_application_preferences, "i", NULL, NULL },
+ { "bug_report", remmina_main_on_action_application_bug_report, NULL, NULL, NULL},
+ { "dark", remmina_main_on_action_application_dark_theme, NULL, NULL, NULL },
+ { "debug", remmina_main_on_action_help_debug, NULL, NULL, NULL },
+ { "community", remmina_main_on_action_help_community, NULL, NULL, NULL },
+ { "donations", remmina_main_on_action_help_donations, NULL, NULL, NULL },
+ { "homepage", remmina_main_on_action_help_homepage, NULL, NULL, NULL },
+ { "wiki", remmina_main_on_action_help_wiki, NULL, NULL, NULL },
+ { "quit", remmina_main_on_action_application_quit, NULL, NULL, NULL },
+};
+
+static GActionEntry main_actions[] = {
+ { "connect", remmina_main_on_action_connection_connect, NULL, NULL, NULL },
+ { "copy", remmina_main_on_action_connection_copy, NULL, NULL, NULL },
+ { "delete", remmina_main_on_action_connection_delete, NULL, NULL, NULL },
+ { "delete_multiple", remmina_main_on_action_connection_delete_multiple, NULL, NULL, NULL },
+ { "edit", remmina_main_on_action_connection_edit, NULL, NULL, NULL },
+ { "exttools", remmina_main_on_action_connection_external_tools, NULL, NULL, NULL },
+ { "new", remmina_main_on_action_connection_new, NULL, NULL, NULL },
+ { "export", remmina_main_on_action_tools_export, NULL, NULL, NULL },
+ { "import", remmina_main_on_action_tools_import, NULL, NULL, NULL },
+ { "expand", remmina_main_on_action_expand, NULL, NULL, NULL },
+ { "collapse", remmina_main_on_action_collapse, NULL, NULL, NULL },
+ { "search", remmina_main_on_action_search_toggle, NULL, NULL, NULL },
+};
+
+static GtkTargetEntry remmina_drop_types[] =
+{
+ { "text/uri-list", 0, 1 }
+};
+
+static char *quick_connect_plugin_list[] =
+{
+ "RDP", "VNC", "SSH", "NX", "SPICE", "X2GO"
+};
+
+/**
+ * Save the Remmina Main Window size to assure the main geometry at each restart
+ */
+static void remmina_main_save_size(void)
+{
+ TRACE_CALL(__func__);
+ if ((gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(remminamain->window))) & GDK_WINDOW_STATE_MAXIMIZED) == 0) {
+ gtk_window_get_size(remminamain->window, &remmina_pref.main_width, &remmina_pref.main_height);
+ remmina_pref.main_maximize = FALSE;
+ } else {
+ remmina_pref.main_maximize = TRUE;
+ }
+}
+
+static void remmina_main_save_expanded_group_func(GtkTreeView *tree_view, GtkTreePath *path, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ gchar *group;
+
+ gtk_tree_model_get_iter(remminamain->priv->file_model_sort, &iter, path);
+ gtk_tree_model_get(remminamain->priv->file_model_sort, &iter, GROUP_COLUMN, &group, -1);
+ if (group) {
+ remmina_string_array_add(remminamain->priv->expanded_group, group);
+ g_free(group);
+ }
+}
+
+static void remmina_main_save_expanded_group(void)
+{
+ TRACE_CALL(__func__);
+ if (GTK_IS_TREE_STORE(remminamain->priv->file_model)) {
+ if (remminamain->priv->expanded_group)
+ remmina_string_array_free(remminamain->priv->expanded_group);
+ remminamain->priv->expanded_group = remmina_string_array_new();
+ gtk_tree_view_map_expanded_rows(remminamain->tree_files_list,
+ (GtkTreeViewMappingFunc)remmina_main_save_expanded_group_func, NULL);
+ }
+}
+
+/**
+ * Save the Remmina Main Window size and the expanded group before to close Remmina.
+ * This function uses remmina_main_save_size and remmina_main_save_expanded_group.
+ */
+void remmina_main_save_before_destroy()
+{
+ TRACE_CALL(__func__);
+ if (!remminamain || !remminamain->window)
+ return;
+
+ remmina_main_save_size();
+ remmina_main_save_expanded_group();
+ g_free(remmina_pref.expanded_group);
+ remmina_pref.expanded_group = remmina_string_array_to_string(remminamain->priv->expanded_group);
+ remmina_pref_save();
+}
+
+void remmina_main_destroy()
+{
+ TRACE_CALL(__func__);
+
+ if (remminamain) {
+ if (remminamain->window)
+ gtk_widget_destroy(GTK_WIDGET(remminamain->window));
+
+ g_object_unref(remminamain->builder);
+ remmina_string_array_free(remminamain->priv->expanded_group);
+ remminamain->priv->expanded_group = NULL;
+ if (remminamain->priv->file_model)
+ g_object_unref(G_OBJECT(remminamain->priv->file_model));
+ g_object_unref(G_OBJECT(remminamain->priv->file_model_filter));
+ g_free(remminamain->priv->selected_filename);
+ g_free(remminamain->priv->selected_name);
+ g_free(remminamain->priv);
+ g_free(remminamain);
+ remminamain = NULL;
+ }
+}
+
+/**
+ * Try to exit remmina after a delete window event
+ */
+static gboolean remmina_main_dexit(gpointer data)
+{
+ TRACE_CALL(__func__);
+ remmina_application_condexit(REMMINA_CONDEXIT_ONMAINWINDELETE);
+ return FALSE;
+}
+
+gboolean remmina_main_on_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ remmina_main_save_before_destroy();
+
+ g_idle_add(remmina_main_dexit, NULL);
+
+ return FALSE;
+}
+
+gboolean remmina_main_idle_destroy(gpointer data)
+{
+ TRACE_CALL(__func__);
+
+ if (remminamain)
+ remmina_main_destroy();
+
+ return G_SOURCE_REMOVE;
+}
+
+/**
+ * Called when the remminamain->window widget is destroyed (glade event handler)
+ */
+void remmina_main_on_destroy_event()
+{
+ TRACE_CALL(__func__);
+
+ if (remminamain) {
+ /* Invalidate remminamain->window to avoid multiple destructions */
+ remminamain->window = NULL;
+ /* Destroy remminamain struct, later. We can't destroy
+ * important objects like the builder now */
+ g_idle_add(remmina_main_idle_destroy, NULL);
+ }
+}
+
+static void remmina_main_clear_selection_data(void)
+{
+ TRACE_CALL(__func__);
+ g_free(remminamain->priv->selected_filename);
+ g_free(remminamain->priv->selected_name);
+ remminamain->priv->selected_filename = NULL;
+ remminamain->priv->selected_name = NULL;
+}
+
+#ifdef SNAP_BUILD
+
+static void remmina_main_show_snap_welcome()
+{
+ GtkBuilder *dlgbuilder = NULL;
+ GtkWidget *dlg;
+ GtkWindow *parent;
+ int result;
+ static gboolean shown_once = FALSE;
+ gboolean need_snap_interface_connections = FALSE;
+ GtkWidget *dsa;
+ RemminaSecretPlugin *remmina_secret_plugin;
+
+ if (shown_once)
+ return;
+ else
+ shown_once = TRUE;
+
+ g_print("Remmina is compiled as a SNAP package.\n");
+ remmina_secret_plugin = remmina_plugin_manager_get_secret_plugin();
+ if (remmina_secret_plugin == NULL) {
+ g_print(" but we can’t find the secret plugin inside the SNAP.\n");
+ need_snap_interface_connections = TRUE;
+ } else {
+ if (!remmina_secret_plugin->is_service_available(remmina_secret_plugin)) {
+ g_print(" but we can’t access a secret service. Secret service or SNAP interface connection is missing.\n");
+ need_snap_interface_connections = TRUE;
+ }
+ }
+
+ if (need_snap_interface_connections && !remmina_pref.prevent_snap_welcome_message) {
+ dlgbuilder = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_snap_info_dialog.glade");
+ dsa = GTK_WIDGET(gtk_builder_get_object(dlgbuilder, "dontshowagain"));
+ if (dlgbuilder) {
+ parent = remmina_main_get_window();
+ dlg = GTK_WIDGET(gtk_builder_get_object(dlgbuilder, "SnapInfoDlg"));
+ if (parent)
+ gtk_window_set_transient_for(GTK_WINDOW(dlg), parent);
+ gtk_builder_connect_signals(dlgbuilder, NULL);
+ result = gtk_dialog_run(GTK_DIALOG(dlg));
+ if (result == 1) {
+ remmina_pref.prevent_snap_welcome_message = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dsa));
+ remmina_pref_save();
+ }
+ gtk_widget_destroy(dlg);
+ g_object_unref(dlgbuilder);
+ }
+ }
+}
+#endif
+
+
+static gboolean remmina_main_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path,
+ gboolean path_currently_selected, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ guint context_id;
+ GtkTreeIter iter;
+ gchar buf[1000];
+
+ if (path_currently_selected)
+ return TRUE;
+
+ if (!gtk_tree_model_get_iter(model, &iter, path))
+ return TRUE;
+
+ remmina_main_clear_selection_data();
+
+ gtk_tree_model_get(model, &iter,
+ NAME_COLUMN, &remminamain->priv->selected_name,
+ FILENAME_COLUMN, &remminamain->priv->selected_filename,
+ -1);
+
+ context_id = gtk_statusbar_get_context_id(remminamain->statusbar_main, "status");
+ gtk_statusbar_pop(remminamain->statusbar_main, context_id);
+ if (remminamain->priv->selected_filename) {
+ g_snprintf(buf, sizeof(buf), "%s (%s)", remminamain->priv->selected_name, remminamain->priv->selected_filename);
+ gtk_statusbar_push(remminamain->statusbar_main, context_id, buf);
+ } else {
+ gtk_statusbar_push(remminamain->statusbar_main, context_id, remminamain->priv->selected_name);
+ }
+
+ return TRUE;
+}
+
+static void remmina_main_load_file_list_callback(RemminaFile *remminafile, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ GtkListStore *store;
+
+ store = GTK_LIST_STORE(user_data);
+ gchar *datetime;
+
+ datetime = remmina_file_get_datetime(remminafile);
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ PROTOCOL_COLUMN, remmina_file_get_icon_name(remminafile),
+ NAME_COLUMN, remmina_file_get_string(remminafile, "name"),
+ NOTES_COLUMN, g_uri_unescape_string(remmina_file_get_string(remminafile, "notes_text"), NULL),
+ GROUP_COLUMN, remmina_file_get_string(remminafile, "group"),
+ SERVER_COLUMN, remmina_file_get_string(remminafile, "server"),
+ PLUGIN_COLUMN, remmina_file_get_string(remminafile, "protocol"),
+ DATE_COLUMN, datetime,
+ FILENAME_COLUMN, remmina_file_get_filename(remminafile),
+ LABELS_COLUMN, remmina_file_get_string(remminafile, "labels"),
+ -1);
+ g_free(datetime);
+}
+
+static gboolean remmina_main_load_file_tree_traverse(GNode *node, GtkTreeStore *store, GtkTreeIter *parent)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter *iter;
+ RemminaGroupData *data;
+ GNode *child;
+
+ iter = NULL;
+ if (node->data) {
+ data = (RemminaGroupData *)node->data;
+ iter = g_new0(GtkTreeIter, 1);
+ gtk_tree_store_append(store, iter, parent);
+ gtk_tree_store_set(store, iter,
+ PROTOCOL_COLUMN, "folder-symbolic",
+ NAME_COLUMN, data->name,
+ GROUP_COLUMN, data->group,
+ DATE_COLUMN, data->datetime,
+ FILENAME_COLUMN, NULL,
+ LABELS_COLUMN, data->labels,
+ -1);
+ }
+ for (child = g_node_first_child(node); child; child = g_node_next_sibling(child))
+ remmina_main_load_file_tree_traverse(child, store, iter);
+ g_free(iter);
+ return FALSE;
+}
+
+static void remmina_main_load_file_tree_group(GtkTreeStore *store)
+{
+ TRACE_CALL(__func__);
+ GNode *root;
+
+ root = remmina_file_manager_get_group_tree();
+ remmina_main_load_file_tree_traverse(root, store, NULL);
+ remmina_file_manager_free_group_tree(root);
+}
+
+static void remmina_main_expand_group_traverse(GtkTreeIter *iter)
+{
+ TRACE_CALL(__func__);
+ GtkTreeModel *tree;
+ gboolean ret;
+ gchar *group, *filename;
+ GtkTreeIter child;
+ GtkTreePath *path;
+
+ tree = remminamain->priv->file_model_sort;
+ ret = TRUE;
+ while (ret) {
+ gtk_tree_model_get(tree, iter, GROUP_COLUMN, &group, FILENAME_COLUMN, &filename, -1);
+ if (filename == NULL) {
+ if (remmina_string_array_find(remminamain->priv->expanded_group, group) >= 0) {
+ path = gtk_tree_model_get_path(tree, iter);
+ gtk_tree_view_expand_row(remminamain->tree_files_list, path, FALSE);
+ gtk_tree_path_free(path);
+ }
+ if (gtk_tree_model_iter_children(tree, &child, iter))
+ remmina_main_expand_group_traverse(&child);
+ }
+ g_free(group);
+ g_free(filename);
+
+ ret = gtk_tree_model_iter_next(tree, iter);
+ }
+}
+
+static void remmina_main_expand_group(void)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter_first(remminamain->priv->file_model_sort, &iter))
+ remmina_main_expand_group_traverse(&iter);
+}
+
+static gboolean remmina_main_load_file_tree_find(GtkTreeModel *tree, GtkTreeIter *iter, const gchar *match_group)
+{
+ TRACE_CALL(__func__);
+ gboolean ret, match;
+ gchar *group, *filename;
+ GtkTreeIter child;
+
+ match = FALSE;
+ ret = TRUE;
+ while (ret) {
+ gtk_tree_model_get(tree, iter, GROUP_COLUMN, &group, FILENAME_COLUMN, &filename, -1);
+ match = (filename == NULL && g_strcmp0(group, match_group) == 0);
+ g_free(group);
+ g_free(filename);
+ if (match)
+ break;
+ if (gtk_tree_model_iter_children(tree, &child, iter)) {
+ match = remmina_main_load_file_tree_find(tree, &child, match_group);
+ if (match) {
+ memcpy(iter, &child, sizeof(GtkTreeIter));
+ break;
+ }
+ }
+ ret = gtk_tree_model_iter_next(tree, iter);
+ }
+ return match;
+}
+
+static void remmina_main_load_file_tree_callback(RemminaFile *remminafile, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter, child;
+ GtkTreeStore *store;
+ gboolean found;
+ gchar *datetime = NULL;
+
+ store = GTK_TREE_STORE(user_data);
+
+ found = FALSE;
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
+ found = remmina_main_load_file_tree_find(GTK_TREE_MODEL(store), &iter,
+ remmina_file_get_string(remminafile, "group"));
+
+ datetime = remmina_file_get_datetime(remminafile);
+ //REMMINA_DEBUG("The date is %s", datetime);
+ gtk_tree_store_append(store, &child, (found ? &iter : NULL));
+ gtk_tree_store_set(store, &child,
+ PROTOCOL_COLUMN, remmina_file_get_icon_name(remminafile),
+ NAME_COLUMN, remmina_file_get_string(remminafile, "name"),
+ NOTES_COLUMN, g_uri_unescape_string(remmina_file_get_string(remminafile, "notes_text"), NULL),
+ GROUP_COLUMN, remmina_file_get_string(remminafile, "group"),
+ SERVER_COLUMN, remmina_file_get_string(remminafile, "server"),
+ PLUGIN_COLUMN, remmina_file_get_string(remminafile, "protocol"),
+ DATE_COLUMN, datetime,
+ FILENAME_COLUMN, remmina_file_get_filename(remminafile),
+ LABELS_COLUMN, remmina_file_get_string(remminafile, "labels"),
+ -1);
+ g_free(datetime);
+}
+
+static void remmina_main_file_model_on_sort(GtkTreeSortable *sortable, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ gint columnid;
+ GtkSortType order;
+
+ gtk_tree_sortable_get_sort_column_id(sortable, &columnid, &order);
+ remmina_pref.main_sort_column_id = columnid;
+ remmina_pref.main_sort_order = order;
+ remmina_pref_save();
+}
+
+static gboolean remmina_main_filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ gchar *text;
+ gchar *protocol, *name, *labels, *group, *server, *plugin, *date, *s;
+ gboolean result = TRUE;
+
+ text = g_ascii_strdown(gtk_entry_get_text(remminamain->entry_quick_connect_server), -1);
+ if (text && text[0]) {
+ gtk_tree_model_get(model, iter,
+ PROTOCOL_COLUMN, &protocol,
+ NAME_COLUMN, &name,
+ GROUP_COLUMN, &group,
+ SERVER_COLUMN, &server,
+ PLUGIN_COLUMN, &plugin,
+ DATE_COLUMN, &date,
+ LABELS_COLUMN, &labels,
+ -1);
+ if (g_strcmp0(protocol, "folder-symbolic") != 0) {
+ s = g_ascii_strdown(name ? name : "", -1);
+ g_free(name);
+ name = s;
+ s = g_ascii_strdown(group ? group : "", -1);
+ g_free(group);
+ group = s;
+ s = g_ascii_strdown(server ? server : "", -1);
+ g_free(server);
+ server = s;
+ s = g_ascii_strdown(plugin ? plugin : "", -1);
+ g_free(plugin);
+ plugin = s;
+ s = g_ascii_strdown(date ? date : "", -1);
+ g_free(date);
+ date = s;
+ result = (strstr(name, text) || strstr(group, text) || strstr(server, text) || strstr(plugin, text) || strstr(date, text));
+
+ // Filter by labels
+
+ s = g_ascii_strdown(labels ? labels : "", -1);
+ g_free(labels);
+ labels = s;
+
+ if (strlen(labels) > 0) {
+ gboolean labels_result = TRUE;
+ gchar **labels_array = g_strsplit(labels, ",", -1);
+ gchar **text_array = g_strsplit(text, ",", -1);
+
+ for (int t = 0; (NULL != text_array[t]); t++) {
+ if (0 == strlen(text_array[t])) {
+ continue;
+ }
+
+ gboolean text_result = FALSE;
+
+ for (int l = 0; (NULL != labels_array[l]); l++) {
+ if (0 == strlen(labels_array[l])) {
+ continue;
+ }
+
+ text_result = (text_result || strstr(labels_array[l], text_array[t]));
+
+ if (text_result) {
+ break;
+ }
+ }
+
+ labels_result = (labels_result && text_result);
+
+ if (!labels_result) {
+ break;
+ }
+ }
+
+ result = (result || labels_result);
+
+ g_strfreev(labels_array);
+ g_strfreev(text_array);
+ }
+ }
+ g_free(protocol);
+ g_free(name);
+ g_free(labels);
+ g_free(group);
+ g_free(server);
+ g_free(plugin);
+ g_free(date);
+ }
+ g_free(text);
+ return result;
+}
+
+static void remmina_main_select_file(const gchar *filename)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gchar *item_filename;
+ gboolean cmp;
+
+ if (!gtk_tree_model_get_iter_first(remminamain->priv->file_model_sort, &iter))
+ return;
+
+ while (TRUE) {
+ gtk_tree_model_get(remminamain->priv->file_model_sort, &iter, FILENAME_COLUMN, &item_filename, -1);
+ cmp = g_strcmp0(item_filename, filename);
+ g_free(item_filename);
+ if (cmp == 0) {
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(remminamain->tree_files_list),
+ &iter);
+ path = gtk_tree_model_get_path(remminamain->priv->file_model_sort, &iter);
+ gtk_tree_view_scroll_to_cell(remminamain->tree_files_list, path, NULL, TRUE, 0.5, 0.0);
+ gtk_tree_path_free(path);
+ return;
+ }
+ if (!gtk_tree_model_iter_next(remminamain->priv->file_model_sort, &iter))
+ return;
+ }
+}
+
+static void remmina_main_load_files()
+{
+ TRACE_CALL(__func__);
+ gint items_count;
+ gchar buf[200];
+ guint context_id;
+ gint view_file_mode;
+ gboolean always_show_notes;
+ char *save_selected_filename;
+ GtkTreeModel *newmodel;
+ const gchar *neticon;
+ const gchar *connection_tooltip;
+
+ save_selected_filename = g_strdup(remminamain->priv->selected_filename);
+ remmina_main_save_expanded_group();
+
+ view_file_mode = remmina_pref.view_file_mode;
+ if (remminamain->priv->override_view_file_mode_to_list)
+ view_file_mode = REMMINA_VIEW_FILE_LIST;
+
+ switch (remmina_pref.view_file_mode) {
+ case REMMINA_VIEW_FILE_TREE:
+ gtk_toggle_button_set_active(remminamain->view_toggle_button, FALSE);
+ break;
+ case REMMINA_VIEW_FILE_LIST:
+ default:
+ gtk_toggle_button_set_active(remminamain->view_toggle_button, TRUE);
+ break;
+ }
+
+ switch (view_file_mode) {
+ case REMMINA_VIEW_FILE_TREE:
+ /* Create new GtkTreeStore model */
+ newmodel = GTK_TREE_MODEL(gtk_tree_store_new(9, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING));
+ /* Hide the Group column in the tree view mode */
+ gtk_tree_view_column_set_visible(remminamain->column_files_list_group, FALSE);
+ /* Load groups first */
+ remmina_main_load_file_tree_group(GTK_TREE_STORE(newmodel));
+ /* Load files list */
+ items_count = remmina_file_manager_iterate((GFunc)remmina_main_load_file_tree_callback, (gpointer)newmodel);
+ break;
+
+ case REMMINA_VIEW_FILE_LIST:
+ default:
+ /* Create new GtkListStore model */
+ newmodel = GTK_TREE_MODEL(gtk_list_store_new(9, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING));
+ /* Show the Group column in the list view mode */
+ gtk_tree_view_column_set_visible(remminamain->column_files_list_group, TRUE);
+ /* Load files list */
+ items_count = remmina_file_manager_iterate((GFunc)remmina_main_load_file_list_callback, (gpointer)newmodel);
+ break;
+ }
+
+ /* Set note column visibility*/
+ always_show_notes = remmina_pref.always_show_notes;
+ if (!always_show_notes){
+ gtk_tree_view_column_set_visible(remminamain->column_files_list_notes, FALSE);
+ }
+
+ /* Unset old model */
+ gtk_tree_view_set_model(remminamain->tree_files_list, NULL);
+
+ /* Destroy the old model and save the new one */
+ remminamain->priv->file_model = newmodel;
+
+ /* Create a sorted filtered model based on newmodel and apply it to the TreeView */
+ remminamain->priv->file_model_filter = gtk_tree_model_filter_new(remminamain->priv->file_model, NULL);
+ gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(remminamain->priv->file_model_filter),
+ (GtkTreeModelFilterVisibleFunc)remmina_main_filter_visible_func, NULL, NULL);
+ remminamain->priv->file_model_sort = gtk_tree_model_sort_new_with_model(remminamain->priv->file_model_filter);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(remminamain->priv->file_model_sort),
+ remmina_pref.main_sort_column_id,
+ remmina_pref.main_sort_order);
+ gtk_tree_view_set_model(remminamain->tree_files_list, remminamain->priv->file_model_sort);
+ g_signal_connect(G_OBJECT(remminamain->priv->file_model_sort), "sort-column-changed",
+ G_CALLBACK(remmina_main_file_model_on_sort), NULL);
+ remmina_main_expand_group();
+ /* Select the file previously selected */
+ if (save_selected_filename) {
+ remmina_main_select_file(save_selected_filename);
+ g_free(save_selected_filename);
+ }
+ gtk_tree_view_column_set_widget(remminamain->column_files_list_date, NULL);
+
+ GtkWidget *label = gtk_tree_view_column_get_button(remminamain->column_files_list_date);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(label),
+ _("The latest successful connection attempt, or a pre-computed date"));
+ /* Show in the status bar the total number of connections found */
+ g_snprintf(buf, sizeof(buf), ngettext("Total %i item.", "Total %i items.", items_count), items_count);
+ context_id = gtk_statusbar_get_context_id(remminamain->statusbar_main, "status");
+ gtk_statusbar_pop(remminamain->statusbar_main, context_id);
+ gtk_statusbar_push(remminamain->statusbar_main, context_id, buf);
+
+ remmina_network_monitor_status (remminamain->monitor);
+ if (remminamain->monitor->connected){
+ neticon = g_strdup("network-transmit-receive-symbolic");
+ connection_tooltip = g_strdup(_("Network status: fully online"));
+ } else {
+ neticon = g_strdup("network-offline-symbolic");
+ connection_tooltip = g_strdup(_("Network status: offline"));
+ }
+
+ if (GTK_IS_WIDGET(remminamain->network_icon))
+ gtk_widget_destroy(remminamain->network_icon);
+ GIcon *icon = g_themed_icon_new (neticon);
+ remminamain->network_icon = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_set_tooltip_text (remminamain->network_icon, connection_tooltip);
+
+ g_object_unref (icon);
+
+ gtk_box_pack_start (GTK_BOX(remminamain->statusbar_main), remminamain->network_icon, FALSE, FALSE, 0);
+ gtk_widget_show (remminamain->network_icon);
+
+}
+
+void remmina_main_load_files_cb(GtkEntry *entry, char *string, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ remmina_main_load_files();
+}
+
+void remmina_main_on_action_connection_connect(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+
+ RemminaFile *remminafile;
+
+ if (!remminamain->priv->selected_filename)
+ return;
+
+ remminafile = remmina_file_load(remminamain->priv->selected_filename);
+
+ if (remminafile == NULL)
+ return;
+
+ if (remmina_pref_get_boolean("use_primary_password")
+ && remmina_pref_get_boolean("lock_connect")
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+ if (remmina_file_get_int (remminafile, "profile-lock", FALSE) == 1
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ remmina_file_touch(remminafile);
+ rcw_open_from_filename(remminamain->priv->selected_filename);
+
+ remmina_file_free(remminafile);
+}
+
+void remmina_main_on_action_connection_external_tools(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ if (!remminamain->priv->selected_filename)
+ return;
+
+ remmina_external_tools_from_filename(remminamain, remminamain->priv->selected_filename);
+}
+
+static void remmina_main_file_editor_destroy(GtkWidget *widget, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+
+ if (!remminamain)
+ return;
+ remmina_main_load_files();
+}
+
+void remmina_main_on_action_application_mpchange(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ RemminaFile *remminafile;
+
+ const gchar *username;
+ const gchar *domain;
+ const gchar *group;
+ const gchar *gatewayusername;
+ const gchar *gatewaydomain;
+
+ username = domain = group = gatewayusername = gatewaydomain = "";
+
+ remminafile = NULL;
+
+ if (remmina_pref_get_boolean("use_primary_password")
+ && remmina_pref_get_boolean("lock_edit")
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ if (remminamain->priv->selected_filename) {
+ remminafile = remmina_file_load(remminamain->priv->selected_filename);
+ if (remminafile != NULL) {
+ username = remmina_file_get_string(remminafile, "username");
+ domain = remmina_file_get_string(remminafile, "domain");
+ group = remmina_file_get_string(remminafile, "group");
+ gatewayusername = remmina_file_get_string(remminafile, "gateway_username");
+ gatewaydomain = remmina_file_get_string(remminafile, "gateway_domain");
+ }
+ }
+
+ remmina_mpchange_schedule(TRUE, group, domain, username, "", gatewayusername, gatewaydomain, "");
+
+ if (remminafile != NULL)
+ remmina_file_free(remminafile);
+}
+
+void remmina_main_on_action_connection_new(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ if (kioskmode && kioskmode == TRUE)
+ return;
+ GtkWidget *widget;
+
+ remmina_plugin_manager_get_available_plugins();
+ if (remmina_pref_get_boolean("use_primary_password")
+ && remmina_pref_get_boolean("lock_edit")
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ widget = remmina_file_editor_new();
+ g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(remmina_main_file_editor_destroy), remminamain);
+ gtk_window_set_transient_for(GTK_WINDOW(widget), remminamain->window);
+ gtk_widget_show(widget);
+ remmina_main_load_files();
+}
+
+static gboolean remmina_main_search_key_event(GtkWidget *search_entry, GdkEventKey *event, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ if (event->keyval == GDK_KEY_Escape) {
+ gtk_entry_set_text(remminamain->entry_quick_connect_server, "");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RM_GET_OBJECT("search_toggle")), FALSE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean remmina_main_tree_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ if (gtk_tree_view_row_expanded(tree, path))
+ gtk_tree_view_collapse_row(tree, path);
+ else
+ gtk_tree_view_expand_row(tree, path, FALSE);
+ return TRUE;
+}
+
+void remmina_main_on_view_toggle()
+{
+ if (gtk_toggle_button_get_active(remminamain->view_toggle_button)) {
+ if (remmina_pref.view_file_mode != REMMINA_VIEW_FILE_LIST) {
+ remmina_pref.view_file_mode = REMMINA_VIEW_FILE_LIST;
+ gtk_entry_set_text(remminamain->entry_quick_connect_server, "");
+ remmina_pref_save();
+ remmina_main_load_files();
+ }
+ } else {
+ if (remmina_pref.view_file_mode != REMMINA_VIEW_FILE_TREE) {
+ remmina_pref.view_file_mode = REMMINA_VIEW_FILE_TREE;
+ gtk_entry_set_text(remminamain->entry_quick_connect_server, "");
+ remmina_pref_save();
+ remmina_main_load_files();
+ }
+ }
+}
+
+void remmina_main_on_action_connection_copy(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkWidget *widget;
+
+ if (remmina_pref_get_boolean("use_primary_password")
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ if (!remminamain->priv->selected_filename)
+ return;
+
+ RemminaFile *remminafile = remmina_file_load(remminamain->priv->selected_filename);
+
+ if (((remmina_pref_get_boolean("lock_edit")
+ && remmina_pref_get_boolean("use_primary_password"))
+ || remmina_file_get_int (remminafile, "profile-lock", FALSE))
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ if (remminafile) {
+ remmina_file_free(remminafile);
+ remminafile = NULL;
+ }
+
+ widget = remmina_file_editor_new_copy(remminamain->priv->selected_filename);
+ if (widget) {
+ g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(remmina_main_file_editor_destroy), remminamain);
+ gtk_window_set_transient_for(GTK_WINDOW(widget), remminamain->window);
+ gtk_widget_show(widget);
+ }
+ /* Select the file previously selected */
+ if (remminamain->priv->selected_filename)
+ remmina_main_select_file(remminamain->priv->selected_filename);
+}
+
+void remmina_main_on_action_connection_edit(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkWidget *widget;
+
+ if (!remminamain->priv->selected_filename)
+ return;
+
+ RemminaFile *remminafile = remmina_file_load(remminamain->priv->selected_filename);
+
+ if (remmina_pref_get_boolean("use_primary_password")
+ && (remmina_pref_get_boolean("lock_edit")
+ || remmina_file_get_int (remminafile, "profile-lock", FALSE))
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ if (remminafile) {
+ remmina_file_free(remminafile);
+ remminafile = NULL;
+ }
+
+ widget = remmina_file_editor_new_from_filename(remminamain->priv->selected_filename);
+ if (widget) {
+ gtk_window_set_transient_for(GTK_WINDOW(widget), remminamain->window);
+ gtk_widget_show(widget);
+ }
+/* Select the file previously selected */
+ if (remminamain->priv->selected_filename)
+ remmina_main_select_file(remminamain->priv->selected_filename);
+}
+
+void remmina_main_on_action_connection_delete(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkWidget *dialog;
+
+ if (!remminamain->priv->selected_filename)
+ return;
+
+ RemminaFile *remminafile = remmina_file_load(remminamain->priv->selected_filename);
+
+ if (((remmina_pref_get_boolean("lock_edit")
+ && remmina_pref_get_boolean("use_primary_password"))
+ || remmina_file_get_int (remminafile, "profile-lock", FALSE))
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ if (remminafile) {
+ remmina_file_free(remminafile);
+ remminafile = NULL;
+ }
+
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ _("Are you sure you want to delete “%s”?"), remminamain->priv->selected_name);
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) {
+ gchar *delfilename = g_strdup(remminamain->priv->selected_filename);
+ remmina_file_delete(delfilename);
+ g_free(delfilename), delfilename = NULL;
+ remmina_icon_populate_menu();
+ remmina_main_load_files();
+ }
+ gtk_widget_destroy(dialog);
+ remmina_main_clear_selection_data();
+}
+
+void remmina_main_on_action_connection_delete_multiple(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkWidget *dialog;
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(remminamain->tree_files_list);
+ GtkTreeModel *model = gtk_tree_view_get_model(remminamain->tree_files_list);
+ GList *list = gtk_tree_selection_get_selected_rows(sel, &model);
+ gchar *file_to_delete;
+
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ _("Are you sure you want to delete the selected files?"));
+
+ // Delete files if Yes is clicked
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) {
+ while (list) {
+ GtkTreePath *path = list->data;
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter(model, &iter, path)) {
+ GtkWidget *dialog_warning;
+ dialog_warning = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
+ _("Failed to delete files!"));
+ gtk_dialog_run(GTK_DIALOG(dialog_warning));
+ gtk_widget_destroy(dialog_warning);
+ gtk_widget_destroy(dialog);
+ remmina_main_clear_selection_data();
+ return;
+ }
+
+ gtk_tree_model_get(model, &iter,
+ FILENAME_COLUMN, &file_to_delete, -1);
+
+ RemminaFile *remminafile = remmina_file_load(file_to_delete);
+
+ if (((remmina_pref_get_boolean("lock_edit")
+ && remmina_pref_get_boolean("use_primary_password"))
+ || remmina_file_get_int (remminafile, "profile-lock", FALSE))
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ if (remminafile) {
+ remmina_file_free(remminafile);
+ remminafile = NULL;
+ }
+
+ gchar *delfilename = g_strdup(file_to_delete);
+ remmina_file_delete(delfilename);
+ g_free(delfilename), delfilename = NULL;
+ remmina_icon_populate_menu();
+ remmina_main_load_files();
+ list = g_list_next(list);
+ }
+ }
+
+ gtk_widget_destroy(dialog);
+ remmina_main_clear_selection_data();
+}
+
+void remmina_main_on_accel_application_preferences(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GVariant *v = g_variant_new("i", 0);
+
+ remmina_main_on_action_application_preferences(NULL, v, NULL);
+}
+
+void remmina_main_reload_preferences()
+{
+ GtkSettings *settings;
+ settings = gtk_settings_get_default();
+ g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
+ if (remminamain) {
+ if(remmina_pref.hide_searchbar){
+ gtk_toggle_button_set_active(remminamain->search_toggle, FALSE);
+ }
+ else{
+ gtk_toggle_button_set_active(remminamain->search_toggle, TRUE);
+ }
+ gtk_tree_view_column_set_visible(remminamain->column_files_list_notes, remmina_pref.always_show_notes);
+ }
+}
+
+void remmina_main_on_action_application_preferences(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+
+ REMMINA_DEBUG("Opening the preferences");
+ gint32 tab_num;
+
+ if (param) {
+ REMMINA_DEBUG("Parameter passed to preferences of type %s", g_variant_get_type_string(param));
+ tab_num = g_variant_get_int32(param);
+ REMMINA_DEBUG("We got a parameter for the preferences: %d", tab_num);
+ } else {
+ tab_num = 0;
+ }
+
+ if (remmina_pref_get_boolean("use_primary_password")
+ && remmina_unlock_new(remminamain->window) == 0)
+ return;
+
+ GtkWidget *widget = remmina_pref_dialog_new(tab_num, remminamain->window);
+
+ gtk_widget_show(widget);
+}
+
+void remmina_main_on_action_application_default(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+#ifndef __APPLE__
+ g_autoptr(GError) error = NULL;
+ GDesktopAppInfo *desktop_info;
+ GAppInfo *info = NULL;
+ g_autofree gchar *id = g_strconcat(REMMINA_APP_ID, ".desktop", NULL);
+ int i;
+
+ desktop_info = g_desktop_app_info_new(id);
+ if (!desktop_info)
+ return;
+
+ info = G_APP_INFO(desktop_info);
+
+ for (i = 0; supported_mime_types[i]; i++) {
+ if (!g_app_info_set_as_default_for_type(info, supported_mime_types[i], &error))
+ g_warning("Failed to set '%s' as the default application for secondary content type '%s': %s",
+ g_app_info_get_name(info), supported_mime_types[i], error->message);
+ else
+ g_debug("Set '%s' as the default application for '%s'",
+ g_app_info_get_name(info),
+ supported_mime_types[i]);
+ }
+#endif
+}
+
+void remmina_main_on_action_application_quit(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ // Called by quit signal in remmina_main.glade
+ TRACE_CALL(__func__);
+ g_debug("Quit intercept");
+ remmina_application_condexit(REMMINA_CONDEXIT_ONQUIT);
+}
+
+void remmina_main_on_date_column_sort_clicked()
+{
+ if (remmina_pref.view_file_mode != REMMINA_VIEW_FILE_LIST) {
+ remmina_pref.view_file_mode = REMMINA_VIEW_FILE_LIST;
+ gtk_entry_set_text(remminamain->entry_quick_connect_server, "");
+ remmina_pref_save();
+ remmina_main_load_files();
+ }
+}
+
+void remmina_main_toggle_password_view(GtkWidget *widget, gpointer data)
+{
+ GtkWindow *mainwindow;
+ gboolean visible = gtk_entry_get_visibility(GTK_ENTRY(widget));
+
+ mainwindow = remmina_main_get_window();
+ if (remmina_pref_get_boolean("use_primary_password") && remmina_pref_get_boolean("lock_view_passwords") && remmina_unlock_new(mainwindow) == 0)
+ return;
+
+ if (visible) {
+ gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
+ gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, "org.remmina.Remmina-password-reveal-symbolic");
+ } else {
+ gtk_entry_set_visibility(GTK_ENTRY(widget), TRUE);
+ gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget), GTK_ENTRY_ICON_SECONDARY, "org.remmina.Remmina-password-conceal-symbolic");
+ }
+}
+
+static void remmina_main_import_file_list(GSList *files)
+{
+ TRACE_CALL(__func__);
+ GtkWidget *dlg;
+ GSList *element;
+ gchar *path;
+ RemminaFilePlugin *plugin;
+ GString *err;
+ RemminaFile *remminafile = NULL;
+ gboolean imported;
+
+ err = g_string_new(NULL);
+ imported = FALSE;
+ for (element = files; element; element = element->next) {
+ path = (gchar *)element->data;
+ plugin = remmina_plugin_manager_get_import_file_handler(path);
+ if (plugin && (remminafile = plugin->import_func(plugin, path)) != NULL && remmina_file_get_string(remminafile, "name")) {
+ remmina_file_generate_filename(remminafile);
+ remmina_file_save(remminafile);
+ imported = TRUE;
+ } else {
+ g_string_append(err, path);
+ g_string_append_c(err, '\n');
+ }
+ if (remminafile) {
+ remmina_file_free(remminafile);
+ remminafile = NULL;
+ }
+ g_free(path);
+ }
+ g_slist_free(files);
+ if (err->len > 0) {
+ // TRANSLATORS: The placeholder %s is an error message
+ dlg = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Unable to import:\n%s"), err->str);
+ g_signal_connect(G_OBJECT(dlg), "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ gtk_widget_show(dlg);
+ }
+ g_string_free(err, TRUE);
+ if (imported)
+ remmina_main_load_files();
+}
+
+static void remmina_main_action_tools_import_on_response(GtkNativeDialog *dialog, gint response_id, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GSList *files;
+
+ if (response_id == GTK_RESPONSE_ACCEPT) {
+ files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+ remmina_main_import_file_list(files);
+ }
+ gtk_native_dialog_destroy(dialog);
+}
+
+static void remmina_set_file_chooser_filters(GtkFileChooser *chooser)
+{
+ GtkFileFilter *filter;
+
+ g_return_if_fail(GTK_IS_FILE_CHOOSER(chooser));
+
+ filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, _("RDP Files"));
+ gtk_file_filter_add_pattern(filter, "*.rdp");
+ gtk_file_filter_add_pattern(filter, "*.rdpx");
+ gtk_file_filter_add_pattern(filter, "*.RDP");
+ gtk_file_filter_add_pattern(filter, "*.RDPX");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), filter);
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(chooser), filter);
+
+ filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, _("All Files"));
+ gtk_file_filter_add_pattern(filter, "*");
+ gtk_file_chooser_add_filter(chooser, filter);
+}
+
+void remmina_main_on_action_tools_import(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkFileChooserNative *chooser;
+
+ chooser = gtk_file_chooser_native_new(_("Import"), remminamain->window,
+ GTK_FILE_CHOOSER_ACTION_OPEN, _("Import"), _("_Cancel"));
+ gtk_native_dialog_set_modal(GTK_NATIVE_DIALOG(chooser), TRUE);
+ remmina_set_file_chooser_filters(GTK_FILE_CHOOSER(chooser));
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), TRUE);
+ g_signal_connect(chooser, "response", G_CALLBACK(remmina_main_action_tools_import_on_response), NULL);
+ gtk_native_dialog_show(GTK_NATIVE_DIALOG(chooser));
+}
+
+static void on_export_save_response (GtkFileChooserNative *dialog, int response, RemminaFile *remminafile)
+{
+ if (response == GTK_RESPONSE_ACCEPT) {
+ RemminaFilePlugin *plugin = remmina_plugin_manager_get_export_file_handler(remminafile);
+ if (plugin){
+ gchar *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ plugin->export_func(plugin, remminafile, path);
+ g_free(path);
+ }
+ }
+ remmina_file_free(remminafile);
+ gtk_native_dialog_destroy(GTK_NATIVE_DIALOG(dialog));
+}
+
+void remmina_main_on_action_tools_export(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ RemminaFilePlugin *plugin;
+ RemminaFile *remminafile;
+ GtkWidget *dialog;
+ GtkFileChooserNative *chooser;
+ gchar *export_name;
+
+ if (!remminamain->priv->selected_filename) {
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Select the connection profile."));
+ g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ gtk_widget_show(dialog);
+ return;
+ }
+
+ remminafile = remmina_file_load(remminamain->priv->selected_filename);
+ if (remminafile == NULL) {
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("Remmina couldn't export."));
+ g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ gtk_widget_show(dialog);
+ return;
+ }
+
+ plugin = remmina_plugin_manager_get_export_file_handler(remminafile);
+ if (plugin) {
+ chooser = gtk_file_chooser_native_new(plugin->export_hints, remminamain->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE, _("_Save"), _("_Cancel"));
+ gtk_native_dialog_set_modal(GTK_NATIVE_DIALOG(chooser), TRUE);
+ remmina_set_file_chooser_filters(GTK_FILE_CHOOSER(chooser));
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE);
+ export_name = g_strdup_printf("%s.rdp", remminamain->priv->selected_name);
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser), export_name);
+ g_free(export_name);
+ g_signal_connect(chooser, "response", G_CALLBACK(on_export_save_response), remminafile);
+ gtk_native_dialog_show(GTK_NATIVE_DIALOG(chooser));
+ } else
+ {
+ remmina_file_free(remminafile);
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("This protocol does not support exporting."));
+ g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ gtk_widget_show(dialog);
+ return;
+ }
+}
+
+void remmina_main_on_action_application_plugins(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ remmina_plugin_manager_get_available_plugins();
+ remmina_plugin_manager_show(remminamain->window);
+}
+
+void remmina_main_on_action_application_dark_theme(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ GtkSettings *settings;
+
+ settings = gtk_settings_get_default();
+
+ if (gtk_switch_get_active(remminamain->switch_dark_mode))
+ remmina_pref.dark_theme = 1;
+ else
+ remmina_pref.dark_theme = 0;
+ remmina_pref_save();
+
+ g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
+}
+
+void remmina_main_on_action_help_homepage(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ g_app_info_launch_default_for_uri("https://www.remmina.org", NULL, NULL);
+}
+
+void remmina_main_on_action_help_wiki(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ g_app_info_launch_default_for_uri("https://gitlab.com/Remmina/Remmina/wikis/home", NULL, NULL);
+}
+
+void remmina_main_on_action_help_community(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ g_app_info_launch_default_for_uri("https://remmina.org/community", NULL, NULL);
+}
+
+void remmina_main_on_action_help_donations(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ g_app_info_launch_default_for_uri("https://www.remmina.org/donations", NULL, NULL);
+}
+
+void remmina_main_on_action_help_debug(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ remmina_log_start();
+}
+
+void remmina_main_on_action_application_about(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ remmina_about_open(remminamain->window);
+};
+
+void remmina_main_on_action_application_bug_report(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ remmina_bug_report_open(remminamain->window);
+};
+
+static gboolean is_empty(const gchar *s)
+{
+ if (s == NULL)
+ return TRUE;
+ while (*s != 0) {
+ if (!isspace((unsigned char)*s))
+ return FALSE;
+ s++;
+ }
+ return TRUE;
+}
+
+static gboolean remmina_main_quickconnect(void)
+{
+ TRACE_CALL(__func__);
+ RemminaFile *remminafile;
+ gchar *server;
+ gchar *server_trimmed;
+ gchar *qcp;
+
+
+ /* Save quick connect protocol if different from the previous one */
+ qcp = gtk_combo_box_text_get_active_text(remminamain->combo_quick_connect_protocol);
+ if (qcp && strcmp(qcp, remmina_pref.last_quickconnect_protocol) != 0) {
+ g_free(remmina_pref.last_quickconnect_protocol);
+ remmina_pref.last_quickconnect_protocol = g_strdup(qcp);
+ remmina_pref_save();
+ }
+
+ remminafile = remmina_file_new();
+ server = g_strdup(gtk_entry_get_text(remminamain->entry_quick_connect_server));
+ if (g_hostname_to_ascii(server) == NULL)
+ return FALSE;
+ /* If server contain /, e.g. vnc://, it won't connect
+ * We could search for an array of invalid characters, but
+ * it's better to find a way to correctly parse and validate addresses
+ */
+ if (g_strrstr(server, "/") != NULL)
+ return FALSE;
+ //if (g_str_has_suffix (server, "/"))
+ //return FALSE;
+ if (is_empty(server))
+ return FALSE;
+
+ /* check if server is an IP address and trim whitespace if so */
+ server_trimmed = g_strdup(server);
+ g_strstrip(server_trimmed);
+ gchar **strings = g_strsplit(server_trimmed, ":", 2);
+
+ if (strings[0] != NULL)
+ if (g_hostname_is_ip_address(strings[0]))
+ g_stpcpy(server, server_trimmed);
+
+ remmina_file_set_string(remminafile, "sound", "off");
+ remmina_file_set_string(remminafile, "server", server);
+ remmina_file_set_string(remminafile, "name", server);
+ remmina_file_set_string(remminafile, "protocol", qcp);
+ g_free(server);
+ g_free(server_trimmed);
+ g_free(qcp);
+
+ rcw_open_from_file(remminafile);
+
+ return FALSE;
+}
+
+gboolean remmina_main_quickconnect_on_click(GtkWidget *widget, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ if (!kioskmode && kioskmode == FALSE)
+ return remmina_main_quickconnect();
+ return FALSE;
+}
+
+/* Select all the text inside the quick search box if there is anything */
+void remmina_main_quick_search_enter(GtkWidget *widget, gpointer user_data)
+{
+ if (gtk_entry_get_text(remminamain->entry_quick_connect_server))
+ gtk_editable_select_region(GTK_EDITABLE(remminamain->entry_quick_connect_server), 0, -1);
+}
+
+void remmina_main_on_action_collapse(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ gtk_tree_view_collapse_all(remminamain->tree_files_list);
+}
+
+void remmina_main_on_action_search_toggle(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ REMMINA_DEBUG("Search toggle triggered");
+
+ gboolean toggle_status = gtk_toggle_button_get_active(remminamain->search_toggle);
+
+ gtk_search_bar_set_search_mode(remminamain->search_bar, toggle_status);
+ if (toggle_status) {
+ REMMINA_DEBUG("Search toggle is active");
+ gtk_widget_grab_focus(GTK_WIDGET(remminamain->entry_quick_connect_server));
+ } else {
+ REMMINA_DEBUG("Search toggle is not active, focus is tree_files_list");
+ gtk_widget_grab_focus(GTK_WIDGET(remminamain->tree_files_list));
+ }
+}
+
+void remmina_main_on_accel_search_toggle(RemminaMain *remminamain)
+{
+ TRACE_CALL(__func__);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remminamain->search_toggle), TRUE);
+}
+
+void remmina_main_on_action_expand(GSimpleAction *action, GVariant *param, gpointer data)
+{
+ TRACE_CALL(__func__);
+ gtk_tree_view_expand_all(remminamain->tree_files_list);
+}
+
+/* Handle double click on a row in the connections list */
+void remmina_main_file_list_on_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+/* If a connection was selected then execute the default action */
+ if (remminamain->priv->selected_filename) {
+ switch (remmina_pref.default_action) {
+ case REMMINA_ACTION_EDIT:
+ remmina_main_on_action_connection_edit(NULL, NULL, NULL);
+ break;
+ case REMMINA_ACTION_CONNECT:
+ default:
+ remmina_main_on_action_connection_connect(NULL, NULL, NULL);
+ break;
+ }
+ }
+}
+
+/* Show the popup menu by the right button mouse click */
+gboolean remmina_main_file_list_on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ if (event->button == MOUSE_BUTTON_RIGHT) {
+ if (!kioskmode && kioskmode == FALSE) {
+#if GTK_CHECK_VERSION(3, 22, 0)
+ // For now, if more than one selected row, display only a delete menu option
+ if (gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(remminamain->tree_files_list)) > 1) {
+ gtk_menu_popup_at_pointer(GTK_MENU(remminamain->menu_popup_delete_rc), (GdkEvent *)event);
+ return GDK_EVENT_STOP;
+ }
+ else {
+ gtk_menu_popup_at_pointer(GTK_MENU(remminamain->menu_popup), (GdkEvent *)event);
+ }
+#else
+ gtk_menu_popup(remminamain->menu_popup, NULL, NULL, NULL, NULL, event->button, event->time);
+#endif
+ }
+ }
+ return FALSE;
+}
+
+/* Show the popup menu by the menu key */
+gboolean remmina_main_file_list_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ if (event->keyval == GDK_KEY_Menu) {
+#if GTK_CHECK_VERSION(3, 22, 0)
+ gtk_menu_popup_at_widget(GTK_MENU(remminamain->menu_popup), widget,
+ GDK_GRAVITY_CENTER, GDK_GRAVITY_CENTER,
+ (GdkEvent *)event);
+#else
+ gtk_menu_popup(remminamain->menu_popup, NULL, NULL, NULL, NULL, 0, event->time);
+#endif
+ }
+ return FALSE;
+}
+
+void remmina_main_quick_search_on_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
+ gtk_entry_set_text(entry, "");
+}
+
+void remmina_main_quick_search_on_changed(GtkEditable *editable, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ /* If a search text was input then temporary set the file mode to list */
+ if (gtk_entry_get_text_length(remminamain->entry_quick_connect_server)) {
+ if (GTK_IS_TREE_STORE(remminamain->priv->file_model)) {
+ /* File view mode changed, put it to override and reload list */
+ remminamain->priv->override_view_file_mode_to_list = TRUE;
+ remmina_main_load_files();
+ }
+ } else {
+ if (remminamain->priv->override_view_file_mode_to_list) {
+ /* File view mode changed, put it to default (disable override) and reload list */
+ remminamain->priv->override_view_file_mode_to_list = FALSE;
+ remmina_main_load_files();
+ }
+ }
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(remminamain->priv->file_model_filter));
+}
+
+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)
+{
+ TRACE_CALL(__func__);
+ gchar **uris;
+ GSList *files = NULL;
+ gint i;
+
+ uris = g_uri_list_extract_uris((const gchar *)gtk_selection_data_get_data(data));
+ for (i = 0; uris[i]; i++) {
+ if (strncmp(uris[i], "file://", 7) != 0)
+ continue;
+ files = g_slist_append(files, g_strdup(uris[i] + 7));
+ }
+ g_strfreev(uris);
+ remmina_main_import_file_list(files);
+}
+
+/* Add a new menuitem to the Tools menu */
+static gboolean remmina_main_add_tool_plugin(gchar *name, RemminaPlugin *plugin, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ RemminaToolPlugin *tool_plugin = (RemminaToolPlugin *)plugin;
+ GtkWidget *menuitem = gtk_menu_item_new_with_label(plugin->description);
+
+ gtk_widget_show(menuitem);
+ gtk_menu_shell_append(GTK_MENU_SHELL(remminamain->menu_popup_full), menuitem);
+ g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(tool_plugin->exec_func), tool_plugin);
+ return FALSE;
+}
+
+gboolean remmina_main_on_window_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ return FALSE;
+}
+
+/* Remmina main window initialization */
+static void remmina_main_init(void)
+{
+ TRACE_CALL(__func__);
+ int i, qcp_idx, qcp_actidx;
+ char *name;
+ GtkSettings *settings;
+
+ REMMINA_DEBUG("Initializing the Remmina main window");
+ /* Switch to a dark theme if the user enabled it */
+ settings = gtk_settings_get_default();
+ g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
+
+ REMMINA_DEBUG ("Initializing monitor");
+ remminamain->monitor = remmina_network_monitor_new();
+
+ remminamain->priv->expanded_group = remmina_string_array_new_from_string(remmina_pref.expanded_group);
+ if (!kioskmode && kioskmode == FALSE)
+ gtk_window_set_title(remminamain->window, _("Remmina Remote Desktop Client"));
+ else
+ gtk_window_set_title(remminamain->window, _("Remmina Kiosk"));
+ if (!kioskmode && kioskmode == FALSE) {
+ gtk_window_set_default_size(remminamain->window, remmina_pref.main_width, remmina_pref.main_height);
+ if (remmina_pref.main_maximize)
+ gtk_window_maximize(remminamain->window);
+ }
+ /* Honor global preferences Search Bar visibility */
+ if (remmina_pref.hide_searchbar)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RM_GET_OBJECT("search_toggle")), FALSE);
+
+ /* Add a GtkMenuItem to the Tools menu for each plugin of type REMMINA_PLUGIN_TYPE_TOOL */
+ remmina_plugin_manager_for_each_plugin(REMMINA_PLUGIN_TYPE_TOOL, remmina_main_add_tool_plugin, remminamain);
+
+ /* Add available quick connect protocols to remminamain->combo_quick_connect_protocol */
+ qcp_idx = qcp_actidx = 0;
+ for (i = 0; i < sizeof(quick_connect_plugin_list) / sizeof(quick_connect_plugin_list[0]); i++) {
+ name = quick_connect_plugin_list[i];
+ if (remmina_plugin_manager_get_plugin(REMMINA_PLUGIN_TYPE_PROTOCOL, name)) {
+ gtk_combo_box_text_append(remminamain->combo_quick_connect_protocol, name, name);
+ if (remmina_pref.last_quickconnect_protocol != NULL && strcmp(name, remmina_pref.last_quickconnect_protocol) == 0)
+ qcp_actidx = qcp_idx;
+ qcp_idx++;
+ }
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(remminamain->combo_quick_connect_protocol), qcp_actidx);
+
+ /* Connect the group accelerators to the GtkWindow */
+ //gtk_window_add_accel_group(remminamain->window, remminamain->accelgroup_shortcuts);
+ /* Set the Quick Connection */
+ gtk_entry_set_activates_default(remminamain->entry_quick_connect_server, TRUE);
+ /* Set the TreeView for the files list */
+ gtk_tree_selection_set_select_function(
+ gtk_tree_view_get_selection(remminamain->tree_files_list),
+ remmina_main_selection_func, NULL, NULL);
+ /** @todo Set entry_quick_connect_server as default search entry. Weirdly. This does not work yet. */
+ gtk_tree_view_set_search_entry(remminamain->tree_files_list, GTK_ENTRY(remminamain->entry_quick_connect_server));
+ if (remmina_pref.hide_searchbar)
+ gtk_widget_grab_focus(GTK_WIDGET(remminamain->tree_files_list));
+ /* Load the files list */
+ remmina_main_load_files();
+
+ /* Drag-n-drop support */
+ gtk_drag_dest_set(GTK_WIDGET(remminamain->window), GTK_DEST_DEFAULT_ALL, remmina_drop_types, 1, GDK_ACTION_COPY);
+
+ /* Finish initialization */
+ remminamain->priv->initialized = TRUE;
+
+ /* Register the window in remmina_widget_pool with GType=GTK_WINDOW and TAG=remmina-main-window */
+ g_object_set_data(G_OBJECT(remminamain->window), "tag", "remmina-main-window");
+ remmina_widget_pool_register(GTK_WIDGET(remminamain->window));
+}
+
+/* Signal handler for "show" on remminamain->window */
+void remmina_main_on_show(GtkWidget *w, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+#ifdef SNAP_BUILD
+ remmina_main_show_snap_welcome();
+#endif
+}
+
+/* RemminaMain instance */
+GtkWidget *remmina_main_new(void)
+{
+ TRACE_CALL(__func__);
+ GSimpleActionGroup *actions;
+ GtkAccelGroup *accel_group = NULL;
+
+ remminamain = g_new0(RemminaMain, 1);
+ remminamain->priv = g_new0(RemminaMainPriv, 1);
+ /* Assign UI widgets to the private members */
+ remminamain->builder = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_main.glade");
+ remminamain->window = GTK_WINDOW(RM_GET_OBJECT("RemminaMain"));
+ if (kioskmode && kioskmode == TRUE) {
+ gtk_window_set_position(remminamain->window, GTK_WIN_POS_CENTER_ALWAYS);
+ gtk_window_set_default_size(remminamain->window, 800, 400);
+ gtk_window_set_resizable(remminamain->window, FALSE);
+ }
+ /* New Button */
+ remminamain->button_new = GTK_BUTTON(RM_GET_OBJECT("button_new"));
+ if (kioskmode && kioskmode == TRUE)
+ gtk_widget_set_sensitive(GTK_WIDGET(remminamain->button_new), FALSE);
+ /* Search bar */
+ remminamain->search_toggle = GTK_TOGGLE_BUTTON(RM_GET_OBJECT("search_toggle"));
+ remminamain->search_bar = GTK_SEARCH_BAR(RM_GET_OBJECT("search_bar"));
+ /* view mode list/tree */
+ remminamain->view_toggle_button = GTK_TOGGLE_BUTTON(RM_GET_OBJECT("view_toggle_button"));
+ if (kioskmode && kioskmode == TRUE)
+ gtk_widget_set_sensitive(GTK_WIDGET(remminamain->view_toggle_button), FALSE);
+
+ /* Menu widgets */
+ remminamain->menu_popup = GTK_MENU(RM_GET_OBJECT("menu_popup"));
+ remminamain->menu_header_button = GTK_MENU_BUTTON(RM_GET_OBJECT("menu_header_button"));
+ remminamain->menu_popup_full = GTK_MENU(RM_GET_OBJECT("menu_popup_full"));
+ remminamain->menu_popup_delete_rc = GTK_MENU(RM_GET_OBJECT("menu_popup_delete_rc"));
+ if (kioskmode && kioskmode == TRUE) {
+ gtk_widget_set_sensitive(GTK_WIDGET(remminamain->menu_popup_full), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(remminamain->menu_header_button), FALSE);
+ }
+ /* View mode radios */
+ remminamain->menuitem_view_mode_list = GTK_RADIO_MENU_ITEM(RM_GET_OBJECT("menuitem_view_mode_list"));
+ remminamain->menuitem_view_mode_tree = GTK_RADIO_MENU_ITEM(RM_GET_OBJECT("menuitem_view_mode_tree"));
+ /* Quick connect objects */
+ remminamain->box_quick_connect = GTK_BOX(RM_GET_OBJECT("box_quick_connect"));
+ remminamain->combo_quick_connect_protocol = GTK_COMBO_BOX_TEXT(RM_GET_OBJECT("combo_quick_connect_protocol"));
+ if (kioskmode && kioskmode == TRUE)
+ gtk_widget_set_sensitive(GTK_WIDGET(remminamain->combo_quick_connect_protocol), FALSE);
+ remminamain->entry_quick_connect_server = GTK_ENTRY(RM_GET_OBJECT("entry_quick_connect_server"));
+ /* Other widgets */
+ remminamain->tree_files_list = GTK_TREE_VIEW(RM_GET_OBJECT("tree_files_list"));
+ remminamain->column_files_list_name = GTK_TREE_VIEW_COLUMN(RM_GET_OBJECT("column_files_list_name"));
+ remminamain->column_files_list_group = GTK_TREE_VIEW_COLUMN(RM_GET_OBJECT("column_files_list_group"));
+ remminamain->column_files_list_server = GTK_TREE_VIEW_COLUMN(RM_GET_OBJECT("column_files_list_server"));
+ remminamain->column_files_list_plugin = GTK_TREE_VIEW_COLUMN(RM_GET_OBJECT("column_files_list_plugin"));
+ remminamain->column_files_list_date = GTK_TREE_VIEW_COLUMN(RM_GET_OBJECT("column_files_list_date"));
+ remminamain->column_files_list_notes = GTK_TREE_VIEW_COLUMN(RM_GET_OBJECT("column_files_list_notes"));
+ gtk_tree_view_column_set_fixed_width(remminamain->column_files_list_notes, 100);
+ remminamain->statusbar_main = GTK_STATUSBAR(RM_GET_OBJECT("statusbar_main"));
+ /* signals */
+ g_signal_connect(remminamain->entry_quick_connect_server, "key-release-event", G_CALLBACK(remmina_main_search_key_event), NULL);
+ g_signal_connect(remminamain->tree_files_list, "row-activated", G_CALLBACK(remmina_main_tree_row_activated), NULL);
+ /* Non widget objects */
+ actions = g_simple_action_group_new();
+ g_action_map_add_action_entries(G_ACTION_MAP(actions), app_actions, G_N_ELEMENTS(app_actions), remminamain->window);
+ gtk_widget_insert_action_group(GTK_WIDGET(remminamain->window), "app", G_ACTION_GROUP(actions));
+ g_action_map_add_action_entries(G_ACTION_MAP(actions), main_actions, G_N_ELEMENTS(main_actions), remminamain->window);
+ gtk_widget_insert_action_group(GTK_WIDGET(remminamain->window), "main", G_ACTION_GROUP(actions));
+ g_object_unref(actions);
+ /* Accelerators */
+ accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(remminamain->window, accel_group);
+ gtk_accel_group_connect(accel_group, GDK_KEY_Q, GDK_CONTROL_MASK, 0,
+ g_cclosure_new_swap(G_CALLBACK(remmina_main_on_action_application_quit), NULL, NULL));
+ // TODO: This crash remmina because the function doesn't receive the parameter we expect
+ gtk_accel_group_connect(accel_group, GDK_KEY_P, GDK_CONTROL_MASK, 0,
+ g_cclosure_new_swap(G_CALLBACK(remmina_main_on_accel_application_preferences), NULL, NULL));
+ gtk_accel_group_connect(accel_group, GDK_KEY_F, GDK_CONTROL_MASK, 0,
+ g_cclosure_new_swap(G_CALLBACK(remmina_main_on_accel_search_toggle), remminamain, NULL));
+
+ /* Connect signals */
+ gtk_builder_connect_signals(remminamain->builder, NULL);
+ /* Initialize the window and load the preferences */
+ remmina_main_init();
+ return GTK_WIDGET(remminamain->window);
+}
+
+GtkWindow *remmina_main_get_window()
+{
+ if (!remminamain)
+ return NULL;
+ if (!remminamain->priv)
+ return NULL;
+ if (!remminamain->priv->initialized)
+ return NULL;
+ remminamain->window = GTK_WINDOW(RM_GET_OBJECT("RemminaMain"));
+ return remminamain->window;
+}
+
+void remmina_main_update_file_datetime(RemminaFile *file)
+{
+ if (!remminamain)
+ return;
+ remmina_main_load_files();
+}
+
+void remmina_main_show_dialog(GtkMessageType msg, GtkButtonsType buttons, const gchar* message) {
+ GtkWidget *dialog;
+
+ if (remminamain->window) {
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, msg, buttons, "%s", message);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ }
+}
+
+void remmina_main_show_warning_dialog(const gchar *message) {
+ GtkWidget *dialog;
+
+ if (remminamain->window) {
+ dialog = gtk_message_dialog_new(remminamain->window, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
+ message, g_get_application_name());
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ }
+} \ No newline at end of file