Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_applet_menu.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include "config.h"
38 
39 #include <gtk/gtk.h>
40 #include <glib/gi18n.h>
41 #include <string.h>
42 
43 #include "remmina_public.h"
45 #include "remmina_applet_menu.h"
46 #include "remmina_file_manager.h"
48 
49 G_DEFINE_TYPE( RemminaAppletMenu, remmina_applet_menu, GTK_TYPE_MENU)
50 
51 struct _RemminaAppletMenuPriv {
52  gboolean hide_count;
53 };
54 
55 enum {
57 };
58 
60 { 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 
69 {
70  TRACE_CALL(__func__);
71  remmina_applet_menu_signals[LAUNCH_ITEM_SIGNAL] = g_signal_new("launch-item", G_TYPE_FROM_CLASS(klass),
72  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaAppletMenuClass, launch_item), NULL, NULL,
73  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),
75  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaAppletMenuClass, edit_item), NULL, NULL,
76  g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
77 }
78 
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 
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,
95  GtkWidget **groupmenuitem)
96 {
97  TRACE_CALL(__func__);
98  GtkWidget *widget;
99  GtkWidget *submenu;
100 
101  widget = gtk_menu_item_new_with_label(group);
102  gtk_widget_show(widget);
103 
104  g_object_set_data_full(G_OBJECT(widget), "group", g_strdup(group), g_free);
105  g_object_set_data(G_OBJECT(widget), "count", GINT_TO_POINTER(0));
106  if (groupmenuitem) {
107  *groupmenuitem = widget;
108  }
109  if (position < 0) {
110  gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget);
111  }else {
112  gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, position);
113  }
114 
115  submenu = gtk_menu_new();
116  gtk_widget_show(submenu);
117  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  gint cnt;
126  gchar *s;
127 
128  cnt = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "count")) + 1;
129  g_object_set_data(G_OBJECT(widget), "count", GINT_TO_POINTER(cnt));
130  s = g_strdup_printf("%s (%i)", (const gchar*)g_object_get_data(G_OBJECT(widget), "group"), cnt);
131  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), s);
132  g_free(s);
133 }
134 
136 {
137  TRACE_CALL(__func__);
138  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_applet_menu_on_item_activate), menu);
139 }
140 
142 {
143  TRACE_CALL(__func__);
144  GtkWidget *submenu;
145  GtkWidget *groupmenuitem;
146  GtkMenuItem *submenuitem;
147  gchar *s, *p1, *p2, *mstr;
148  GList *childs, *child;
149  gint position;
150 
151  submenu = GTK_WIDGET(menu);
152  s = g_strdup(menuitem->group);
153  p1 = s;
154  p2 = p1 ? strchr(p1, '/') : NULL;
155  if (p2)
156  *p2++ = '\0';
157  while (p1 && p1[0]) {
158  groupmenuitem = NULL;
159  childs = gtk_container_get_children(GTK_CONTAINER(submenu));
160  position = -1;
161  for (child = g_list_first(childs); child; child = g_list_next(child)) {
162  if (!GTK_IS_MENU_ITEM(child->data))
163  continue;
164  position++;
165  submenuitem = GTK_MENU_ITEM(child->data);
166  if (gtk_menu_item_get_submenu(submenuitem)) {
167  mstr = (gchar*)g_object_get_data(G_OBJECT(submenuitem), "group");
168  if (g_strcmp0(p1, mstr) == 0) {
169  /* Found existing group menu */
170  submenu = gtk_menu_item_get_submenu(submenuitem);
171  groupmenuitem = GTK_WIDGET(submenuitem);
172  break;
173  }else {
174  /* Redo comparison ignoring case and respecting international
175  * collation, to set menu sort order */
176  if (strcoll(p1, mstr) < 0) {
177  submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem,
178  &groupmenuitem);
179  break;
180  }
181  }
182  }else {
183  submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem, &groupmenuitem);
184  break;
185  }
186 
187  }
188 
189  if (!child) {
190  submenu = remmina_applet_menu_add_group(submenu, p1, -1, menuitem, &groupmenuitem);
191  }
192  g_list_free(childs);
193  if (groupmenuitem && !menu->priv->hide_count) {
195  }
196  p1 = p2;
197  p2 = p1 ? strchr(p1, '/') : NULL;
198  if (p2)
199  *p2++ = '\0';
200  }
201  g_free(s);
202 
203  childs = gtk_container_get_children(GTK_CONTAINER(submenu));
204  position = -1;
205  for (child = g_list_first(childs); child; child = g_list_next(child)) {
206  if (!GTK_IS_MENU_ITEM(child->data))
207  continue;
208  position++;
209  submenuitem = GTK_MENU_ITEM(child->data);
210  if (gtk_menu_item_get_submenu(submenuitem))
211  continue;
212  if (!REMMINA_IS_APPLET_MENU_ITEM(submenuitem))
213  continue;
214  if (strcoll(menuitem->name, REMMINA_APPLET_MENU_ITEM(submenuitem)->name) <= 0) {
215  gtk_menu_shell_insert(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem), position);
216  break;
217  }
218  }
219  if (!child) {
220  gtk_menu_shell_append(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem));
221  }
222  g_list_free(childs);
223  remmina_applet_menu_register_item(menu, menuitem);
224 }
225 
226 GtkWidget*
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 
238 {
239  TRACE_CALL(__func__);
240  menu->priv->hide_count = hide_count;
241 }
242 
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  remmina_data_dir = remmina_file_get_datadir();
253  dir = g_dir_open(remmina_data_dir, 0, NULL);
254  if (dir != NULL) {
255  /* Iterate all remote desktop profiles */
256  while ((name = g_dir_read_name(dir)) != NULL) {
257  if (!g_str_has_suffix(name, ".remmina"))
258  continue;
259  g_snprintf(filename, sizeof(filename), "%s/%s", remmina_data_dir, name);
260 
262  if (menuitem != NULL) {
263  remmina_applet_menu_add_item(menu, REMMINA_APPLET_MENU_ITEM(menuitem));
264  gtk_widget_show(menuitem);
265  }
266  }
267  g_dir_close(dir);
268  }
269  g_free(remmina_data_dir);
270 }
271 
static void remmina_applet_menu_class_init(RemminaAppletMenuClass *klass)
static void remmina_applet_menu_init(RemminaAppletMenu *menu)
struct _RemminaAppletMenuPriv RemminaAppletMenuPriv
static GtkWidget * remmina_applet_menu_add_group(GtkWidget *menu, const gchar *group, gint position, RemminaAppletMenuItem *menuitem, GtkWidget **groupmenuitem)
GtkWidget * remmina_applet_menu_new(void)
static void remmina_applet_menu_increase_group_count(GtkWidget *widget)
void remmina_applet_menu_set_hide_count(RemminaAppletMenu *menu, gboolean hide_count)
static guint remmina_applet_menu_signals[LAST_SIGNAL]
void remmina_applet_menu_populate(RemminaAppletMenu *menu)
gchar * remmina_file_get_datadir(void)
GtkWidget * remmina_applet_menu_item_new(RemminaAppletMenuItemType item_type,...)
G_DEFINE_TYPE(RemminaAppletMenu, remmina_applet_menu, GTK_TYPE_MENU)
RemminaAppletMenuPriv * priv
void remmina_applet_menu_add_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
void remmina_applet_menu_register_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
static void remmina_applet_menu_on_item_activate(RemminaAppletMenuItem *menuitem, RemminaAppletMenu *menu)
static void remmina_applet_menu_destroy(RemminaAppletMenu *menu, gpointer data)