summaryrefslogtreecommitdiffstats
path: root/src/remmina_string_list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/remmina_string_list.c')
-rw-r--r--src/remmina_string_list.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/src/remmina_string_list.c b/src/remmina_string_list.c
new file mode 100644
index 0000000..0fafacb
--- /dev/null
+++ b/src/remmina_string_list.c
@@ -0,0 +1,312 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2009 - Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
+ *
+ * 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 <string.h>
+#include "config.h"
+#include "remmina_public.h"
+#include "remmina_string_list.h"
+#include "remmina/remmina_trace_calls.h"
+
+static RemminaStringList *string_list;
+
+#define COLUMN_DESCRIPTION 0
+#define COLUMN_VALUE 1
+#define GET_OBJECT(object_name) gtk_builder_get_object(string_list->builder, object_name)
+
+/* Update the buttons state on the items in the TreeModel */
+void remmina_string_list_update_buttons_state(void)
+{
+ gint items_count = gtk_tree_model_iter_n_children(
+ GTK_TREE_MODEL(string_list->liststore_items), NULL);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(string_list->button_remove), items_count > 0);
+ gtk_widget_set_sensitive(GTK_WIDGET(string_list->button_up), items_count > 1);
+ gtk_widget_set_sensitive(GTK_WIDGET(string_list->button_down), items_count > 1);
+}
+
+/* Check the text inserted in the list */
+void remmina_string_list_on_cell_edited(GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text)
+{
+ TRACE_CALL(__func__);
+ gchar *text;
+ gchar *error;
+ GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(string_list->liststore_items), &iter, path);
+ /* Remove delimitors from the string */
+ text = remmina_public_str_replace(new_text, STRING_DELIMITOR, " ");
+ if (cell == string_list->cellrenderertext_item1) {
+ gtk_list_store_set(string_list->liststore_items, &iter, COLUMN_DESCRIPTION, text, -1);
+ }else {
+ /* Check for validation only in second field */
+ if (string_list->priv->validation_func) {
+ if (!((*string_list->priv->validation_func)(text, &error))) {
+ gtk_label_set_text(string_list->label_status, error);
+ gtk_widget_show(GTK_WIDGET(string_list->label_status));
+ g_free(error);
+ }else {
+ gtk_widget_hide(GTK_WIDGET(string_list->label_status));
+ }
+ }
+ gtk_list_store_set(string_list->liststore_items, &iter, COLUMN_VALUE, text, -1);
+ }
+ gtk_tree_path_free(path);
+ g_free(text);
+}
+
+/* Move a TreeIter position */
+static void remmina_string_list_move_iter(GtkTreeIter *from, GtkTreeIter *to)
+{
+ TRACE_CALL(__func__);
+ GtkTreePath *path;
+
+ gtk_list_store_swap(string_list->liststore_items, from, to);
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(string_list->liststore_items), from);
+ gtk_tree_view_scroll_to_cell(string_list->treeview_items, path, NULL, 0, 0, 0);
+ gtk_tree_path_free(path);
+}
+
+/* Move down the selected TreeRow */
+void remmina_string_list_on_action_down(GtkWidget *widget, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ GtkTreeIter target_iter;
+
+ if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
+ gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &target_iter);
+ if (gtk_tree_model_iter_next(GTK_TREE_MODEL(string_list->liststore_items), &target_iter)) {
+ remmina_string_list_move_iter(&iter, &target_iter);
+ }
+ }
+}
+
+/* Move up the selected TreeRow */
+void remmina_string_list_on_action_up(GtkWidget *widget, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ GtkTreeIter target_iter;
+ GtkTreePath *path;
+
+ if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
+ gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &target_iter);
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(string_list->liststore_items), &target_iter);
+ /* Before moving the TreeRow check if there’s a previous item */
+ if (gtk_tree_path_prev(path)) {
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(string_list->liststore_items), &target_iter, path);
+ gtk_tree_path_free(path);
+ remmina_string_list_move_iter(&iter, &target_iter);
+ }
+ }
+}
+
+/* Add a new TreeRow to the list */
+void remmina_string_list_on_action_add(GtkWidget *widget, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ gtk_list_store_append(string_list->liststore_items, &iter);
+ gtk_tree_selection_select_iter(string_list->treeview_selection, &iter);
+
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(string_list->liststore_items), &iter);
+ gtk_tree_view_set_cursor_on_cell(string_list->treeview_items, path,
+ string_list->treeviewcolumn_item,
+ GTK_CELL_RENDERER(string_list->priv->two_columns ? string_list->cellrenderertext_item1 : string_list->cellrenderertext_item2),
+ TRUE);
+ gtk_tree_path_free(path);
+ remmina_string_list_update_buttons_state();
+}
+
+/* Remove the selected TreeRow from the list */
+void remmina_string_list_on_action_remove(GtkWidget *widget, gpointer user_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
+ gtk_list_store_remove(string_list->liststore_items, &iter);
+ }
+ gtk_widget_hide(GTK_WIDGET(string_list->label_status));
+ remmina_string_list_update_buttons_state();
+}
+
+/* Load a string list by splitting a string value */
+void remmina_string_list_set_text(const gchar *text, const gboolean clear_data)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ gchar **items;
+ gchar **values;
+ gint i;
+ /* Clear the data before to load new items */
+ if (clear_data)
+ gtk_list_store_clear(string_list->liststore_items);
+ /* Split the string and insert each snippet in the string list */
+ items = g_strsplit(text, STRING_DELIMITOR, -1);
+ for (i = 0; i < g_strv_length(items); i++) {
+ values = g_strsplit(items[i], string_list->priv->fields_separator, -1);
+ gtk_list_store_append(string_list->liststore_items, &iter);
+ if (g_strv_length(values) > 1) {
+ /* Two columns data */
+ gtk_list_store_set(string_list->liststore_items, &iter,
+ COLUMN_DESCRIPTION, values[0],
+ COLUMN_VALUE, values[1],
+ -1);
+ }else {
+ /* Single column data */
+ gtk_list_store_set(string_list->liststore_items, &iter,
+ COLUMN_VALUE, values[0],
+ -1);
+ }
+ g_strfreev(values);
+ }
+ g_strfreev(items);
+ remmina_string_list_update_buttons_state();
+}
+
+/* Get a string value representing the string list */
+gchar* remmina_string_list_get_text(void)
+{
+ TRACE_CALL(__func__);
+ GString *str;
+ GtkTreeIter iter;
+ gboolean first;
+ gboolean ret;
+ const gchar *item_description;
+ const gchar *item_value;
+
+ str = g_string_new(NULL);
+ first = TRUE;
+ /* Cycle each GtkTreeIter in the ListStore */
+ ret = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(string_list->liststore_items), &iter);
+ while (ret) {
+ gtk_tree_model_get(GTK_TREE_MODEL(string_list->liststore_items), &iter,
+ COLUMN_DESCRIPTION, &item_description,
+ COLUMN_VALUE, &item_value,
+ -1);
+ if (!item_description)
+ item_description = "";
+ if (item_value && strlen(item_value) > 0) {
+ /* Add a delimitor after the first element */
+ if (!first) {
+ g_string_append(str, STRING_DELIMITOR);
+ }else {
+ first = FALSE;
+ }
+ /* Add the description for two columns list */
+ if (string_list->priv->two_columns) {
+ g_string_append(str, item_description);
+ g_string_append(str, string_list->priv->fields_separator);
+ }
+ /* Add the element to the string */
+ g_string_append(str, item_value);
+ }
+ ret = gtk_tree_model_iter_next(GTK_TREE_MODEL(string_list->liststore_items), &iter);
+ }
+ return g_string_free(str, FALSE);
+}
+
+/* Set a function that will be used to validate the new rows */
+void remmina_string_list_set_validation_func(RemminaStringListValidationFunc func)
+{
+ TRACE_CALL(__func__);
+ string_list->priv->validation_func = func;
+}
+
+/* Set the dialog titles */
+void remmina_string_list_set_titles(gchar *title1, gchar *title2)
+{
+ /* Set dialog titlebar */
+ gtk_window_set_title(GTK_WINDOW(string_list->dialog),
+ (title1 && strlen(title1) > 0) ? title1 : "");
+ /* Set title label */
+ if (title2 && strlen(title2) > 0) {
+ gtk_label_set_text(string_list->label_title, title2);
+ gtk_widget_show(GTK_WIDGET(string_list->label_title));
+ }else {
+ gtk_widget_hide(GTK_WIDGET(string_list->label_title));
+ }
+}
+
+/* RemminaStringList initialization */
+static void remmina_string_list_init(void)
+{
+ TRACE_CALL(__func__);
+ string_list->priv->validation_func = NULL;
+ /* When two columns are requested, show also the first column */
+ if (string_list->priv->two_columns)
+ gtk_cell_renderer_set_visible(GTK_CELL_RENDERER(string_list->cellrenderertext_item1), TRUE);
+ remmina_string_list_update_buttons_state();
+}
+
+/* RemminaStringList instance */
+GtkDialog* remmina_string_list_new(gboolean two_columns, const gchar *fields_separator)
+{
+ TRACE_CALL(__func__);
+ string_list = g_new0(RemminaStringList, 1);
+ string_list->priv = g_new0(RemminaStringListPriv, 1);
+
+ string_list->builder = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_string_list.glade");
+ string_list->dialog = GTK_DIALOG(gtk_builder_get_object(string_list->builder, "DialogStringList"));
+
+ string_list->liststore_items = GTK_LIST_STORE(GET_OBJECT("liststore_items"));
+ string_list->treeview_items = GTK_TREE_VIEW(GET_OBJECT("treeview_items"));
+ string_list->treeviewcolumn_item = GTK_TREE_VIEW_COLUMN(GET_OBJECT("treeviewcolumn_item"));
+ string_list->treeview_selection = GTK_TREE_SELECTION(GET_OBJECT("treeview_selection"));
+ string_list->cellrenderertext_item1 = GTK_CELL_RENDERER_TEXT(GET_OBJECT("cellrenderertext_item1"));
+ string_list->cellrenderertext_item2 = GTK_CELL_RENDERER_TEXT(GET_OBJECT("cellrenderertext_item2"));
+ string_list->button_add = GTK_BUTTON(GET_OBJECT("button_add"));
+ string_list->button_remove = GTK_BUTTON(GET_OBJECT("button_remove"));
+ string_list->button_up = GTK_BUTTON(GET_OBJECT("button_up"));
+ string_list->button_down = GTK_BUTTON(GET_OBJECT("button_down"));
+ string_list->label_title = GTK_LABEL(GET_OBJECT("label_title"));
+ string_list->label_status = GTK_LABEL(GET_OBJECT("label_status"));
+
+ /* Connect signals */
+ gtk_builder_connect_signals(string_list->builder, NULL);
+ /* Initialize the window and load the values */
+ if (!fields_separator)
+ fields_separator = STRING_DELIMITOR2;
+ string_list->priv->fields_separator = fields_separator;
+ string_list->priv->two_columns = two_columns;
+ remmina_string_list_init();
+
+ return string_list->dialog;
+}