remmina_applet_menu.c 8.38 KB
Newer Older
Antenore Gatta's avatar
Antenore Gatta committed
1 2 3 4
/*
 * Remmina - The GTK+ Remote Desktop Client
 * Copyright (C) 2010 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 "config.h"

39 40 41
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <string.h>
42

43
#include "remmina_public.h"
44 45
#include "remmina_applet_menu_item.h"
#include "remmina_applet_menu.h"
46
#include "remmina_file_manager.h"
47
#include "remmina/remmina_trace_calls.h"
48

49
G_DEFINE_TYPE( RemminaAppletMenu, remmina_applet_menu, GTK_TYPE_MENU)
50

Antenore Gatta's avatar
Antenore Gatta committed
51
struct _RemminaAppletMenuPriv {
52
	gboolean hide_count;
53 54
};

Antenore Gatta's avatar
Antenore Gatta committed
55
enum {
56
	LAUNCH_ITEM_SIGNAL, EDIT_ITEM_SIGNAL, LAST_SIGNAL
57 58
};

59 60
static guint remmina_applet_menu_signals[LAST_SIGNAL] =
{ 0 };
61

62
static void remmina_applet_menu_destroy(RemminaAppletMenu *menu, gpointer data)
63
{
64
	TRACE_CALL(__func__);
65
	g_free(menu->priv);
66 67
}

68
static void remmina_applet_menu_class_init(RemminaAppletMenuClass *klass)
69
{
70
	TRACE_CALL(__func__);
71
	remmina_applet_menu_signals[LAUNCH_ITEM_SIGNAL] = g_signal_new("launch-item", G_TYPE_FROM_CLASS(klass),
Antenore Gatta's avatar
Antenore Gatta committed
72 73
		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaAppletMenuClass, launch_item), NULL, NULL,
		g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
74
	remmina_applet_menu_signals[EDIT_ITEM_SIGNAL] = g_signal_new("edit-item", G_TYPE_FROM_CLASS(klass),
Antenore Gatta's avatar
Antenore Gatta committed
75 76
		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaAppletMenuClass, edit_item), NULL, NULL,
		g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
77 78
}

79
static void remmina_applet_menu_init(RemminaAppletMenu *menu)
80
{
81
	TRACE_CALL(__func__);
82
	menu->priv = g_new0(RemminaAppletMenuPriv, 1);
83

84
	g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(remmina_applet_menu_destroy), NULL);
85 86
}

87
static void remmina_applet_menu_on_item_activate(RemminaAppletMenuItem *menuitem, RemminaAppletMenu *menu)
88
{
89
	TRACE_CALL(__func__);
90
	g_signal_emit(G_OBJECT(menu), remmina_applet_menu_signals[LAUNCH_ITEM_SIGNAL], 0, menuitem);
91 92 93
}

static GtkWidget*
94
remmina_applet_menu_add_group(GtkWidget *menu, const gchar *group, gint position, RemminaAppletMenuItem *menuitem,
Antenore Gatta's avatar
Antenore Gatta committed
95
			      GtkWidget **groupmenuitem)
96
{
97
	TRACE_CALL(__func__);
98 99
	GtkWidget *widget;
	GtkWidget *submenu;
100

101
	widget = gtk_menu_item_new_with_label(group);
102
	gtk_widget_show(widget);
103

104 105
	g_object_set_data_full(G_OBJECT(widget), "group", g_strdup(group), g_free);
	g_object_set_data(G_OBJECT(widget), "count", GINT_TO_POINTER(0));
Antenore Gatta's avatar
Antenore Gatta committed
106
	if (groupmenuitem) {
107 108
		*groupmenuitem = widget;
	}
Antenore Gatta's avatar
Antenore Gatta committed
109
	if (position < 0) {
110
		gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget);
Antenore Gatta's avatar
Antenore Gatta committed
111
	}else  {
112 113
		gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, position);
	}
114

115 116 117
	submenu = gtk_menu_new();
	gtk_widget_show(submenu);
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu);
118

119
	return submenu;
120 121
}

122
static void remmina_applet_menu_increase_group_count(GtkWidget *widget)
123
{
124
	TRACE_CALL(__func__);
125 126
	gint cnt;
	gchar *s;
127

128 129
	cnt = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "count")) + 1;
	g_object_set_data(G_OBJECT(widget), "count", GINT_TO_POINTER(cnt));
Antenore Gatta's avatar
Antenore Gatta committed
130
	s = g_strdup_printf("%s (%i)", (const gchar*)g_object_get_data(G_OBJECT(widget), "group"), cnt);
131 132
	gtk_menu_item_set_label(GTK_MENU_ITEM(widget), s);
	g_free(s);
133 134
}

135
void remmina_applet_menu_register_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
136
{
137
	TRACE_CALL(__func__);
138
	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_applet_menu_on_item_activate), menu);
139 140
}

141
void remmina_applet_menu_add_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
142
{
143
	TRACE_CALL(__func__);
144 145 146
	GtkWidget *submenu;
	GtkWidget *groupmenuitem;
	GtkMenuItem *submenuitem;
147
	gchar *s, *p1, *p2, *mstr;
148 149
	GList *childs, *child;
	gint position;
150

151 152 153 154 155 156
	submenu = GTK_WIDGET(menu);
	s = g_strdup(menuitem->group);
	p1 = s;
	p2 = p1 ? strchr(p1, '/') : NULL;
	if (p2)
		*p2++ = '\0';
Antenore Gatta's avatar
Antenore Gatta committed
157
	while (p1 && p1[0]) {
158 159 160
		groupmenuitem = NULL;
		childs = gtk_container_get_children(GTK_CONTAINER(submenu));
		position = -1;
Antenore Gatta's avatar
Antenore Gatta committed
161
		for (child = g_list_first(childs); child; child = g_list_next(child)) {
162 163 164 165
			if (!GTK_IS_MENU_ITEM(child->data))
				continue;
			position++;
			submenuitem = GTK_MENU_ITEM(child->data);
Antenore Gatta's avatar
Antenore Gatta committed
166 167 168
			if (gtk_menu_item_get_submenu(submenuitem)) {
				mstr = (gchar*)g_object_get_data(G_OBJECT(submenuitem), "group");
				if (g_strcmp0(p1, mstr) == 0) {
169 170 171 172
					/* Found existing group menu */
					submenu = gtk_menu_item_get_submenu(submenuitem);
					groupmenuitem = GTK_WIDGET(submenuitem);
					break;
Antenore Gatta's avatar
Antenore Gatta committed
173
				}else  {
174 175
					/* Redo comparison ignoring case and respecting international
					 * collation, to set menu sort order */
Antenore Gatta's avatar
Antenore Gatta committed
176
					if (strcoll(p1, mstr) < 0) {
177
						submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem,
Antenore Gatta's avatar
Antenore Gatta committed
178
							&groupmenuitem);
179 180
						break;
					}
181
				}
Antenore Gatta's avatar
Antenore Gatta committed
182
			}else  {
183 184 185
				submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem, &groupmenuitem);
				break;
			}
186

187
		}
188

Antenore Gatta's avatar
Antenore Gatta committed
189
		if (!child) {
190 191 192
			submenu = remmina_applet_menu_add_group(submenu, p1, -1, menuitem, &groupmenuitem);
		}
		g_list_free(childs);
Antenore Gatta's avatar
Antenore Gatta committed
193
		if (groupmenuitem && !menu->priv->hide_count) {
194 195 196 197 198 199 200 201
			remmina_applet_menu_increase_group_count(groupmenuitem);
		}
		p1 = p2;
		p2 = p1 ? strchr(p1, '/') : NULL;
		if (p2)
			*p2++ = '\0';
	}
	g_free(s);
202

203 204
	childs = gtk_container_get_children(GTK_CONTAINER(submenu));
	position = -1;
Antenore Gatta's avatar
Antenore Gatta committed
205
	for (child = g_list_first(childs); child; child = g_list_next(child)) {
206 207 208 209 210 211 212 213
		if (!GTK_IS_MENU_ITEM(child->data))
			continue;
		position++;
		submenuitem = GTK_MENU_ITEM(child->data);
		if (gtk_menu_item_get_submenu(submenuitem))
			continue;
		if (!REMMINA_IS_APPLET_MENU_ITEM(submenuitem))
			continue;
Antenore Gatta's avatar
Antenore Gatta committed
214
		if (strcoll(menuitem->name, REMMINA_APPLET_MENU_ITEM(submenuitem)->name) <= 0) {
215 216 217 218
			gtk_menu_shell_insert(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem), position);
			break;
		}
	}
Antenore Gatta's avatar
Antenore Gatta committed
219
	if (!child) {
220 221
		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem));
	}
222
	g_list_free(childs);
223
	remmina_applet_menu_register_item(menu, menuitem);
224 225 226
}

GtkWidget*
227
remmina_applet_menu_new(void)
228
{
229
	TRACE_CALL(__func__);
230
	RemminaAppletMenu *menu;
231

232
	menu = REMMINA_APPLET_MENU(g_object_new(REMMINA_TYPE_APPLET_MENU, NULL));
233

234
	return GTK_WIDGET(menu);
235 236
}

237
void remmina_applet_menu_set_hide_count(RemminaAppletMenu *menu, gboolean hide_count)
238
{
239
	TRACE_CALL(__func__);
240
	menu->priv->hide_count = hide_count;
241 242
}

243
void remmina_applet_menu_populate(RemminaAppletMenu *menu)
244
{
245
	TRACE_CALL(__func__);
246
	GtkWidget *menuitem;
247
	gchar filename[MAX_PATH_LEN];
248
	GDir *dir;
249
	gchar *remmina_data_dir;
250
	const gchar *name;
251

252 253
	remmina_data_dir = remmina_file_get_datadir();
	dir = g_dir_open(remmina_data_dir, 0, NULL);
Antenore Gatta's avatar
Antenore Gatta committed
254
	if (dir != NULL) {
255
		/* Iterate all remote desktop profiles */
Antenore Gatta's avatar
Antenore Gatta committed
256
		while ((name = g_dir_read_name(dir)) != NULL) {
257 258
			if (!g_str_has_suffix(name, ".remmina"))
				continue;
259
			g_snprintf(filename, sizeof(filename), "%s/%s", remmina_data_dir, name);
260

261
			menuitem = remmina_applet_menu_item_new(REMMINA_APPLET_MENU_ITEM_FILE, filename);
Antenore Gatta's avatar
Antenore Gatta committed
262
			if (menuitem != NULL) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
263 264 265
				remmina_applet_menu_add_item(menu, REMMINA_APPLET_MENU_ITEM(menuitem));
				gtk_widget_show(menuitem);
			}
266 267 268
		}
		g_dir_close(dir);
	}
269
	g_free(remmina_data_dir);
270 271
}