diff options
Diffstat (limited to 'src/remmina_applet_menu.c')
-rw-r--r-- | src/remmina_applet_menu.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/remmina_applet_menu.c b/src/remmina_applet_menu.c new file mode 100644 index 0000000..fa1362f --- /dev/null +++ b/src/remmina_applet_menu.c @@ -0,0 +1,283 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2010 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 "config.h" + +#include <glib/gi18n.h> +#include <string.h> + +#include "remmina_public.h" +#include "remmina_applet_menu.h" +#include "remmina_file_manager.h" +#include "remmina_pref.h" +#include "remmina/remmina_trace_calls.h" + +G_DEFINE_TYPE( RemminaAppletMenu, remmina_applet_menu, GTK_TYPE_MENU) + +struct _RemminaAppletMenuPriv { + gboolean hide_count; +}; + +enum { + LAUNCH_ITEM_SIGNAL, EDIT_ITEM_SIGNAL, LAST_SIGNAL +}; + +static guint remmina_applet_menu_signals[LAST_SIGNAL] = +{ 0 }; + +static void remmina_applet_menu_destroy(RemminaAppletMenu *menu, gpointer data) +{ + TRACE_CALL(__func__); + g_free(menu->priv); +} + +static void remmina_applet_menu_class_init(RemminaAppletMenuClass *klass) +{ + TRACE_CALL(__func__); + remmina_applet_menu_signals[LAUNCH_ITEM_SIGNAL] = g_signal_new("launch-item", G_TYPE_FROM_CLASS(klass), + 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); + remmina_applet_menu_signals[EDIT_ITEM_SIGNAL] = g_signal_new("edit-item", G_TYPE_FROM_CLASS(klass), + 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); +} + +static void remmina_applet_menu_init(RemminaAppletMenu *menu) +{ + TRACE_CALL(__func__); + menu->priv = g_new0(RemminaAppletMenuPriv, 1); + + g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(remmina_applet_menu_destroy), NULL); +} + +static void remmina_applet_menu_on_item_activate(RemminaAppletMenuItem *menuitem, RemminaAppletMenu *menu) +{ + TRACE_CALL(__func__); + g_signal_emit(G_OBJECT(menu), remmina_applet_menu_signals[LAUNCH_ITEM_SIGNAL], 0, menuitem); +} + +static GtkWidget* +remmina_applet_menu_add_group(GtkWidget *menu, const gchar *group, gint position, RemminaAppletMenuItem *menuitem, + GtkWidget **groupmenuitem) +{ + TRACE_CALL(__func__); + GtkWidget *widget; + GtkWidget *submenu; + + widget = gtk_menu_item_new_with_label(group); + gtk_widget_show(widget); + + 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)); + if (groupmenuitem) { + *groupmenuitem = widget; + } + if (position < 0) { + gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget); + }else { + gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, position); + } + + submenu = gtk_menu_new(); + gtk_widget_show(submenu); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu); + + return submenu; +} + +static void remmina_applet_menu_increase_group_count(GtkWidget *widget) +{ + TRACE_CALL(__func__); + gint cnt; + gchar *s; + + 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)); + s = g_strdup_printf("%s (%i)", (const gchar*)g_object_get_data(G_OBJECT(widget), "group"), cnt); + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), s); + g_free(s); +} + +void remmina_applet_menu_register_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem) +{ + TRACE_CALL(__func__); + g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_applet_menu_on_item_activate), menu); +} + +void remmina_applet_menu_add_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem) +{ + TRACE_CALL(__func__); + GtkWidget *submenu; + GtkWidget *groupmenuitem; + GtkMenuItem *submenuitem; + gchar *s, *p1, *p2, *mstr; + GList *childs, *child; + gint position; + + submenu = GTK_WIDGET(menu); + s = g_strdup(menuitem->group); + p1 = s; + p2 = p1 ? strchr(p1, '/') : NULL; + if (p2) + *p2++ = '\0'; + while (p1 && p1[0]) { + groupmenuitem = NULL; + childs = gtk_container_get_children(GTK_CONTAINER(submenu)); + position = -1; + for (child = g_list_first(childs); child; child = g_list_next(child)) { + if (!GTK_IS_MENU_ITEM(child->data)) + continue; + position++; + submenuitem = GTK_MENU_ITEM(child->data); + if (gtk_menu_item_get_submenu(submenuitem)) { + mstr = (gchar*)g_object_get_data(G_OBJECT(submenuitem), "group"); + if (g_strcmp0(p1, mstr) == 0) { + /* Found existing group menu */ + submenu = gtk_menu_item_get_submenu(submenuitem); + groupmenuitem = GTK_WIDGET(submenuitem); + break; + }else { + /* Redo comparison ignoring case and respecting international + * collation, to set menu sort order */ + if (strcoll(p1, mstr) < 0) { + submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem, + &groupmenuitem); + break; + } + } + }else { + submenu = remmina_applet_menu_add_group(submenu, p1, position, menuitem, &groupmenuitem); + break; + } + + } + + if (!child) { + submenu = remmina_applet_menu_add_group(submenu, p1, -1, menuitem, &groupmenuitem); + } + g_list_free(childs); + if (groupmenuitem && !menu->priv->hide_count) { + remmina_applet_menu_increase_group_count(groupmenuitem); + } + p1 = p2; + p2 = p1 ? strchr(p1, '/') : NULL; + if (p2) + *p2++ = '\0'; + } + g_free(s); + + childs = gtk_container_get_children(GTK_CONTAINER(submenu)); + position = -1; + for (child = g_list_first(childs); child; child = g_list_next(child)) { + 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; + if (strcoll(menuitem->name, REMMINA_APPLET_MENU_ITEM(submenuitem)->name) <= 0) { + gtk_menu_shell_insert(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem), position); + break; + } + } + if (!child) { + gtk_menu_shell_append(GTK_MENU_SHELL(submenu), GTK_WIDGET(menuitem)); + } + g_list_free(childs); + remmina_applet_menu_register_item(menu, menuitem); +} + +GtkWidget* +remmina_applet_menu_new(void) +{ + TRACE_CALL(__func__); + RemminaAppletMenu *menu; + + menu = REMMINA_APPLET_MENU(g_object_new(REMMINA_TYPE_APPLET_MENU, NULL)); + + return GTK_WIDGET(menu); +} + +void remmina_applet_menu_set_hide_count(RemminaAppletMenu *menu, gboolean hide_count) +{ + TRACE_CALL(__func__); + menu->priv->hide_count = hide_count; +} + +void remmina_applet_menu_populate(RemminaAppletMenu *menu) +{ + TRACE_CALL(__func__); + GtkWidget *menuitem; + gchar filename[MAX_PATH_LEN]; + GDir *dir; + gchar *remmina_data_dir; + const gchar *name; + gint count = 0; + + gboolean new_ontop = remmina_pref.applet_new_ontop; + + remmina_data_dir = remmina_file_get_datadir(); + dir = g_dir_open(remmina_data_dir, 0, NULL); + if (dir != NULL) { + /* Iterate all remote desktop profiles */ + while ((name = g_dir_read_name(dir)) != NULL) { + if (!g_str_has_suffix(name, ".remmina")) + continue; + g_snprintf(filename, sizeof(filename), "%s/%s", remmina_data_dir, name); + + menuitem = remmina_applet_menu_item_new(REMMINA_APPLET_MENU_ITEM_FILE, filename); + if (menuitem != NULL) { + remmina_applet_menu_add_item(menu, REMMINA_APPLET_MENU_ITEM(menuitem)); + gtk_widget_show(menuitem); + count++; + } + } + g_dir_close(dir); + if (count > 0) { + /* Separator */ + menuitem = gtk_separator_menu_item_new(); + gtk_widget_show(menuitem); + if (new_ontop) + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem); + else + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + g_free(remmina_data_dir); +} + |