remmina_string_list.c 11.4 KB
Newer Older
Antenore Gatta's avatar
Antenore Gatta committed
1 2 3 4
/*
 * Remmina - The GTK+ Remote Desktop Client
 * Copyright (C) 2009 - Vic Lee
 * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5
 * Copyright (C) 2016-2018 Antenore Gatta, Giovanni Panozzo
Antenore Gatta's avatar
Antenore Gatta committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 * 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.
 *
 */
36 37 38

#include <gtk/gtk.h>
#include <string.h>
39
#include "config.h"
40 41
#include "remmina_public.h"
#include "remmina_string_list.h"
42
#include "remmina/remmina_trace_calls.h"
43

44
static RemminaStringList *string_list;
45

46 47
#define COLUMN_DESCRIPTION 0
#define COLUMN_VALUE 1
48
#define GET_OBJECT(object_name) gtk_builder_get_object(string_list->builder, object_name)
49

50 51 52 53
/* 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(
Antenore Gatta's avatar
Antenore Gatta committed
54
		GTK_TREE_MODEL(string_list->liststore_items), NULL);
55

56 57 58 59 60
	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);
}

61 62
/* Check the text inserted in the list */
void remmina_string_list_on_cell_edited(GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text)
63
{
64
	TRACE_CALL(__func__);
65 66 67
	gchar *text;
	gchar *error;
	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
68 69
	GtkTreeIter iter;

70 71 72
	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, " ");
Antenore Gatta's avatar
Antenore Gatta committed
73
	if (cell == string_list->cellrenderertext_item1) {
74
		gtk_list_store_set(string_list->liststore_items, &iter, COLUMN_DESCRIPTION, text, -1);
Antenore Gatta's avatar
Antenore Gatta committed
75
	}else  {
76
		/* Check for validation only in second field */
Antenore Gatta's avatar
Antenore Gatta committed
77 78
		if (string_list->priv->validation_func) {
			if (!((*string_list->priv->validation_func)(text, &error))) {
79 80 81
				gtk_label_set_text(string_list->label_status, error);
				gtk_widget_show(GTK_WIDGET(string_list->label_status));
				g_free(error);
Antenore Gatta's avatar
Antenore Gatta committed
82
			}else  {
83 84
				gtk_widget_hide(GTK_WIDGET(string_list->label_status));
			}
85
		}
86
		gtk_list_store_set(string_list->liststore_items, &iter, COLUMN_VALUE, text, -1);
87
	}
88 89
	gtk_tree_path_free(path);
	g_free(text);
90 91
}

92 93
/* Move a TreeIter position */
static void remmina_string_list_move_iter(GtkTreeIter *from, GtkTreeIter *to)
94
{
95
	TRACE_CALL(__func__);
96
	GtkTreePath *path;
97

98 99 100
	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);
101
	gtk_tree_path_free(path);
102 103
}

104 105
/* Move down the selected TreeRow */
void remmina_string_list_on_action_down(GtkWidget *widget, gpointer user_data)
106
{
107
	TRACE_CALL(__func__);
108 109 110
	GtkTreeIter iter;
	GtkTreeIter target_iter;

Antenore Gatta's avatar
Antenore Gatta committed
111
	if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
112
		gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &target_iter);
Antenore Gatta's avatar
Antenore Gatta committed
113
		if (gtk_tree_model_iter_next(GTK_TREE_MODEL(string_list->liststore_items), &target_iter)) {
114
			remmina_string_list_move_iter(&iter, &target_iter);
115 116
		}
	}
117 118
}

119 120
/* Move up the selected TreeRow */
void remmina_string_list_on_action_up(GtkWidget *widget, gpointer user_data)
121
{
122
	TRACE_CALL(__func__);
123 124 125 126
	GtkTreeIter iter;
	GtkTreeIter target_iter;
	GtkTreePath *path;

Antenore Gatta's avatar
Antenore Gatta committed
127
	if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
128 129 130
		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 */
Antenore Gatta's avatar
Antenore Gatta committed
131
		if (gtk_tree_path_prev(path)) {
132
			gtk_tree_model_get_iter(GTK_TREE_MODEL(string_list->liststore_items), &target_iter, path);
133
			gtk_tree_path_free(path);
134
			remmina_string_list_move_iter(&iter, &target_iter);
135 136
		}
	}
137 138
}

139 140
/* Add a new TreeRow to the list */
void remmina_string_list_on_action_add(GtkWidget *widget, gpointer user_data)
141
{
142
	TRACE_CALL(__func__);
143
	GtkTreeIter iter;
144
	GtkTreePath *path;
145

146 147
	gtk_list_store_append(string_list->liststore_items, &iter);
	gtk_tree_selection_select_iter(string_list->treeview_selection, &iter);
148

149
	path = gtk_tree_model_get_path(GTK_TREE_MODEL(string_list->liststore_items), &iter);
150
	gtk_tree_view_set_cursor_on_cell(string_list->treeview_items, path,
Antenore Gatta's avatar
Antenore Gatta committed
151 152 153
		string_list->treeviewcolumn_item,
		GTK_CELL_RENDERER(string_list->priv->two_columns ? string_list->cellrenderertext_item1 : string_list->cellrenderertext_item2),
		TRUE);
154
	gtk_tree_path_free(path);
155
	remmina_string_list_update_buttons_state();
156 157
}

158 159
/* Remove the selected TreeRow from the list */
void remmina_string_list_on_action_remove(GtkWidget *widget, gpointer user_data)
160
{
161
	TRACE_CALL(__func__);
162
	GtkTreeIter iter;
163

Antenore Gatta's avatar
Antenore Gatta committed
164
	if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
165
		gtk_list_store_remove(string_list->liststore_items, &iter);
166
	}
167
	gtk_widget_hide(GTK_WIDGET(string_list->label_status));
168
	remmina_string_list_update_buttons_state();
169 170
}

171 172
/* Load a string list by splitting a string value */
void remmina_string_list_set_text(const gchar *text, const gboolean clear_data)
173
{
174
	TRACE_CALL(__func__);
175
	GtkTreeIter iter;
176
	gchar **items;
177
	gchar **values;
178 179 180 181 182 183
	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);
Antenore Gatta's avatar
Antenore Gatta committed
184
	for (i = 0; i < g_strv_length(items); i++) {
185
		values = g_strsplit(items[i], string_list->priv->fields_separator, -1);
186
		gtk_list_store_append(string_list->liststore_items, &iter);
Antenore Gatta's avatar
Antenore Gatta committed
187
		if (g_strv_length(values) > 1) {
188 189
			/* Two columns data */
			gtk_list_store_set(string_list->liststore_items, &iter,
Antenore Gatta's avatar
Antenore Gatta committed
190 191 192 193
				COLUMN_DESCRIPTION, values[0],
				COLUMN_VALUE, values[1],
				-1);
		}else  {
194 195
			/* Single column data */
			gtk_list_store_set(string_list->liststore_items, &iter,
Antenore Gatta's avatar
Antenore Gatta committed
196 197
				COLUMN_VALUE, values[0],
				-1);
198 199
		}
		g_strfreev(values);
200
	}
201
	g_strfreev(items);
202
	remmina_string_list_update_buttons_state();
203 204
}

205 206
/* Get a string value representing the string list */
gchar* remmina_string_list_get_text(void)
207
{
208
	TRACE_CALL(__func__);
209 210
	GString *str;
	GtkTreeIter iter;
211 212
	gboolean first;
	gboolean ret;
213 214
	const gchar *item_description;
	const gchar *item_value;
215 216 217

	str = g_string_new(NULL);
	first = TRUE;
218 219
	/* Cycle each GtkTreeIter in the ListStore */
	ret = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(string_list->liststore_items), &iter);
Antenore Gatta's avatar
Antenore Gatta committed
220
	while (ret) {
221
		gtk_tree_model_get(GTK_TREE_MODEL(string_list->liststore_items), &iter,
Antenore Gatta's avatar
Antenore Gatta committed
222 223 224
			COLUMN_DESCRIPTION, &item_description,
			COLUMN_VALUE, &item_value,
			-1);
225 226
		if (!item_description)
			item_description = "";
Antenore Gatta's avatar
Antenore Gatta committed
227
		if (item_value && strlen(item_value) > 0) {
228
			/* Add a delimitor after the first element */
Antenore Gatta's avatar
Antenore Gatta committed
229
			if (!first) {
230
				g_string_append(str, STRING_DELIMITOR);
Antenore Gatta's avatar
Antenore Gatta committed
231
			}else  {
232
				first = FALSE;
233
			}
234
			/* Add the description for two columns list */
Antenore Gatta's avatar
Antenore Gatta committed
235
			if (string_list->priv->two_columns) {
236 237 238
				g_string_append(str, item_description);
				g_string_append(str, string_list->priv->fields_separator);
			}
239
			/* Add the element to the string */
240
			g_string_append(str, item_value);
241
		}
242
		ret = gtk_tree_model_iter_next(GTK_TREE_MODEL(string_list->liststore_items), &iter);
243 244
	}
	return g_string_free(str, FALSE);
245 246
}

247 248
/* Set a function that will be used to validate the new rows */
void remmina_string_list_set_validation_func(RemminaStringListValidationFunc func)
249
{
250
	TRACE_CALL(__func__);
251
	string_list->priv->validation_func = func;
252 253
}

254 255 256 257 258
/* 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),
Antenore Gatta's avatar
Antenore Gatta committed
259
		(title1 && strlen(title1) > 0) ? title1 : "");
260
	/* Set title label */
Antenore Gatta's avatar
Antenore Gatta committed
261
	if (title2 && strlen(title2) > 0) {
262 263
		gtk_label_set_text(string_list->label_title, title2);
		gtk_widget_show(GTK_WIDGET(string_list->label_title));
Antenore Gatta's avatar
Antenore Gatta committed
264
	}else  {
265 266 267 268 269
		gtk_widget_hide(GTK_WIDGET(string_list->label_title));
	}
}

/* RemminaStringList initialization */
270
static void remmina_string_list_init(void)
271
{
272
	TRACE_CALL(__func__);
273
	string_list->priv->validation_func = NULL;
274 275 276
	/* 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);
277
	remmina_string_list_update_buttons_state();
278 279 280
}

/* RemminaStringList instance */
281
GtkDialog* remmina_string_list_new(gboolean two_columns, const gchar *fields_separator)
282
{
283
	TRACE_CALL(__func__);
284 285 286 287 288 289 290 291
	string_list = g_new0(RemminaStringList, 1);
	string_list->priv = g_new0(RemminaStringListPriv, 1);

	string_list->builder = remmina_public_gtk_builder_new_from_file("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"));
292
	string_list->treeviewcolumn_item = GTK_TREE_VIEW_COLUMN(GET_OBJECT("treeviewcolumn_item"));
293
	string_list->treeview_selection = GTK_TREE_SELECTION(GET_OBJECT("treeview_selection"));
294 295
	string_list->cellrenderertext_item1 = GTK_CELL_RENDERER_TEXT(GET_OBJECT("cellrenderertext_item1"));
	string_list->cellrenderertext_item2 = GTK_CELL_RENDERER_TEXT(GET_OBJECT("cellrenderertext_item2"));
296 297 298 299 300 301 302 303 304 305
	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 */
306 307 308 309
	if (!fields_separator)
		fields_separator = STRING_DELIMITOR2;
	string_list->priv->fields_separator = fields_separator;
	string_list->priv->two_columns = two_columns;
310 311 312 313
	remmina_string_list_init();

	return string_list->dialog;
}