Remmina - The GTK+ Remote Desktop Client  v1.4.33
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-2023 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 <glib/gi18n.h>
40 #include <string.h>
41 
42 #include "remmina_public.h"
43 #include "remmina_applet_menu.h"
44 #include "remmina_file_manager.h"
45 #include "remmina_pref.h"
47 
48 G_DEFINE_TYPE( RemminaAppletMenu, remmina_applet_menu, GTK_TYPE_MENU)
49 
50 struct _RemminaAppletMenuPriv {
51  gboolean hide_count;
52 };
53 
54 enum {
56 };
57 
59 { 0 };
60 
61 static void remmina_applet_menu_destroy(RemminaAppletMenu *menu, gpointer data)
62 {
63  TRACE_CALL(__func__);
64  g_free(menu->priv);
65 }
66 
68 {
69  TRACE_CALL(__func__);
70  remmina_applet_menu_signals[LAUNCH_ITEM_SIGNAL] = g_signal_new("launch-item", G_TYPE_FROM_CLASS(klass),
71  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaAppletMenuClass, launch_item), NULL, NULL,
72  g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
73  remmina_applet_menu_signals[EDIT_ITEM_SIGNAL] = g_signal_new("edit-item", G_TYPE_FROM_CLASS(klass),
74  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaAppletMenuClass, edit_item), NULL, NULL,
75  g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
76 }
77 
79 {
80  TRACE_CALL(__func__);
81  menu->priv = g_new0(RemminaAppletMenuPriv, 1);
82 
83  g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(remmina_applet_menu_destroy), NULL);
84 }
85 
87 {
88  TRACE_CALL(__func__);
89  g_signal_emit(G_OBJECT(menu), remmina_applet_menu_signals[LAUNCH_ITEM_SIGNAL], 0, menuitem);
90 }
91 
92 static GtkWidget*
93 remmina_applet_menu_add_group(GtkWidget *menu, const gchar *group, gint position, RemminaAppletMenuItem *menuitem,
94  GtkWidget **groupmenuitem)
95 {
96  TRACE_CALL(__func__);
97  GtkWidget *widget;
98  GtkWidget *submenu;
99 
100  widget = gtk_menu_item_new_with_label(group);
101  gtk_widget_show(widget);
102 
103  g_object_set_data_full(G_OBJECT(widget), "group", g_strdup(group), g_free);
104  g_object_set_data(G_OBJECT(widget), "count", GINT_TO_POINTER(0));
105  if (groupmenuitem) {
106  *groupmenuitem = widget;
107  }
108  if (position < 0) {
109  gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget);
110  }else {
111  gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, position);
112  }
113 
114  submenu = gtk_menu_new();
115  gtk_widget_show(submenu);
116  gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu);
117 
118  return submenu;
119 }
120 
121 static void remmina_applet_menu_increase_group_count(GtkWidget *widget)
122 {
123  TRACE_CALL(__func__);
124  gint cnt;
125  gchar *s;
126 
127  cnt = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "count")) + 1;
128  g_object_set_data(G_OBJECT(widget), "count", GINT_TO_POINTER(cnt));
129  s = g_strdup_printf("%s (%i)", (const gchar*)g_object_get_data(G_OBJECT(widget), "group"), cnt);
130  gtk_menu_item_set_label(GTK_MENU_ITEM(widget), s);
131  g_free(s);
132 }
133 
135 {
136  TRACE_CALL(__func__);
137  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_applet_menu_on_item_activate), menu);
138 }
139 
141 {
142  TRACE_CALL(__func__);
143  GtkWidget *submenu;
144  GtkWidget *groupmenuitem;
145  GtkMenuItem *submenuitem;
146  gchar *s, *p1, *p2, *mstr;
147  GList *childs, *child;
148  gint position;
149 
150  submenu = GTK_WIDGET(menu);
151  s = g_strdup(menuitem->group);
152  p1 = s;
153  p2 = p1 ? strchr(p1, '/') : NULL;
154  if (p2)
155  *p2++ = '\0';
156  while (p1 && p1[0]) {
157  groupmenuitem = NULL;
158  childs = gtk_container_get_children(GTK_CONTAINER(submenu));
159  position = -1;
160  for (child = g_list_first(childs); child; child = g_list_next(child)) {
161  if (!GTK_IS_MENU_ITEM(child->data))
162  continue;
163  position++;
164  submenuitem = GTK_MENU_ITEM(child->data);
165  if (gtk_menu_item_get_submenu(submenuitem)) {
166  mstr = (gchar*)g_object_get_data(G_OBJECT(submenuitem), "group");
167  if (g_strcmp0(p1, mstr) == 0) {
168  /* Found existing group menu */
169  submenu = gtk_menu_item_get_submenu(submenuitem);
170  groupmenuitem = GTK_WIDGET(submenuitem);
171  break;
172  }else {
173  /* Redo comparison ignoring case and respecting international
174  * collation, to set menu sort order */
175  if (strcoll(p1, mstr) < 0) {
176  submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem,
177  &groupmenuitem);
178  break;
179  }
180  }
181  }else {
182  submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem, &groupmenuitem);
183  break;
184  }
185 
186  }
187 
188  if (!child) {
189  submenu = remmina_applet_menu_add_group(submenu, p1, -1, menuitem, &groupmenuitem);
190  }
191  g_list_free(childs);
192  if (groupmenuitem && !menu->priv->hide_count) {
194  }
195  p1 = p2;
196  p2 = p1 ? strchr(p1, '/') : NULL;
197  if (p2)
198  *p2++ = '\0';
199  }
200  g_free(s);
201 
202  childs = gtk_container_get_children(GTK_CONTAINER(submenu));
203  position = -1;
204  for (child = g_list_first(childs); child; child = g_list_next(child)) {
205  if (!GTK_IS_MENU_ITEM(child->data))
206  continue;
207  position++;
208  submenuitem = GTK_MENU_ITEM(child->data);
209  if (gtk_menu_item_get_submenu(submenuitem))
210  continue;
211  if (!REMMINA_IS_APPLET_MENU_ITEM(submenuitem))
212  continue;
213  if (strcoll(menuitem->name, REMMINA_APPLET_MENU_ITEM(submenuitem)->name) <= 0) {
214  gtk_menu_shell_insert(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem), position);
215  break;
216  }
217  }
218  if (!child) {
219  gtk_menu_shell_append(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem));
220  }
221  g_list_free(childs);
222  remmina_applet_menu_register_item(menu, menuitem);
223 }
224 
225 GtkWidget*
227 {
228  TRACE_CALL(__func__);
229  RemminaAppletMenu *menu;
230 
231  menu = REMMINA_APPLET_MENU(g_object_new(REMMINA_TYPE_APPLET_MENU, NULL));
232 
233  return GTK_WIDGET(menu);
234 }
235 
237 {
238  TRACE_CALL(__func__);
239  menu->priv->hide_count = hide_count;
240 }
241 
243 {
244  TRACE_CALL(__func__);
245  GtkWidget *menuitem;
246  gchar filename[MAX_PATH_LEN];
247  GDir *dir;
248  gchar *remmina_data_dir;
249  const gchar *name;
250  gint count = 0;
251 
252  gboolean new_ontop = remmina_pref.applet_new_ontop;
253 
254  remmina_data_dir = remmina_file_get_datadir();
255  dir = g_dir_open(remmina_data_dir, 0, NULL);
256  if (dir != NULL) {
257  /* Iterate all remote desktop profiles */
258  while ((name = g_dir_read_name(dir)) != NULL) {
259  if (!g_str_has_suffix(name, ".remmina"))
260  continue;
261  g_snprintf(filename, sizeof(filename), "%s/%s", remmina_data_dir, name);
262 
264  if (menuitem != NULL) {
265  remmina_applet_menu_add_item(menu, REMMINA_APPLET_MENU_ITEM(menuitem));
266  gtk_widget_show(menuitem);
267  count++;
268  }
269  }
270  g_dir_close(dir);
271  if (count > 0) {
272  /* Separator */
273  menuitem = gtk_separator_menu_item_new();
274  gtk_widget_show(menuitem);
275  if (new_ontop)
276  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
277  else
278  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
279  }
280  }
281  g_free(remmina_data_dir);
282 }
283 
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)
Return datadir_path from pref or first found data dir as per XDG specs.
RemminaPref remmina_pref
Definition: rcw.c:79
gboolean applet_new_ontop
Definition: remmina_pref.h:162
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)