Chandra
view release on metacpan or search on metacpan
include/webview-tray-gtk.c view on Meta::CPAN
/*
* webview-tray-gtk.c - Linux system tray (GtkStatusIcon) backend
*
* Uses GtkStatusIcon (deprecated but widely supported) with GtkMenu.
* Falls back gracefully if no system tray is available.
*/
struct webview_tray_gtk {
GtkStatusIcon *icon;
GtkWidget *menu;
};
/* Forward */
static void tray_gtk_activate(GtkStatusIcon *icon, gpointer user_data);
static void tray_gtk_popup(GtkStatusIcon *icon, guint button,
guint activate_time, gpointer user_data);
/* Menu item activation callback */
static void tray_gtk_menu_item_activated(GtkMenuItem *menuitem,
gpointer user_data) {
int *item_data = (int *)user_data;
/* item_data[0] = item_id, item_data[1..] = pointer to tray */
/* We pack item_id and tray pointer together */
(void)menuitem;
/* This approach is fragile. Instead, use g_object_set_data. */
}
/* Alternative: use g_object_set_data to attach callback info */
struct tray_gtk_cb_data {
struct webview_tray *tray;
int item_id;
};
static void tray_gtk_item_activated(GtkMenuItem *menuitem, gpointer data) {
(void)menuitem;
struct tray_gtk_cb_data *cb = (struct tray_gtk_cb_data *)data;
if (cb && cb->tray && cb->tray->menu_cb) {
cb->tray->menu_cb(cb->tray->w, cb->item_id);
}
}
static GtkWidget *tray_gtk_build_menu(struct webview_tray *t,
struct webview_tray_item *items,
int count) {
GtkWidget *menu = gtk_menu_new();
for (int i = 0; i < count; i++) {
struct webview_tray_item *it = &items[i];
GtkWidget *mi;
if (it->is_separator) {
mi = gtk_separator_menu_item_new();
} else if (it->is_checked) {
mi = gtk_check_menu_item_new_with_label(it->label ? it->label : "");
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
} else {
mi = gtk_menu_item_new_with_label(it->label ? it->label : "");
}
if (it->is_disabled) {
gtk_widget_set_sensitive(mi, FALSE);
}
if (it->submenu_count > 0 && it->submenu && !it->is_separator) {
GtkWidget *sub = tray_gtk_build_menu(t, it->submenu, it->submenu_count);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), sub);
} else if (!it->is_separator) {
/* Connect signal for leaf items */
struct tray_gtk_cb_data *cb = (struct tray_gtk_cb_data *)
g_malloc(sizeof(struct tray_gtk_cb_data));
cb->tray = t;
cb->item_id = it->id;
g_signal_connect_data(G_OBJECT(mi), "activate",
G_CALLBACK(tray_gtk_item_activated),
cb, (GClosureNotify)g_free, 0);
}
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
}
gtk_widget_show_all(menu);
return menu;
}
static void tray_gtk_activate(GtkStatusIcon *icon, gpointer user_data) {
(void)icon;
struct webview_tray *t = (struct webview_tray *)user_data;
/* Left click - invoke item 0 if it exists, or just show menu */
if (t->menu_cb && t->item_count > 0) {
t->menu_cb(t->w, -1); /* -1 = tray icon clicked */
}
}
static void tray_gtk_popup(GtkStatusIcon *icon, guint button,
guint activate_time, gpointer user_data) {
struct webview_tray *t = (struct webview_tray *)user_data;
struct webview_tray_gtk *priv = (struct webview_tray_gtk *)t->_priv;
if (priv->menu) {
gtk_menu_popup(GTK_MENU(priv->menu), NULL, NULL,
gtk_status_icon_position_menu, icon, button, activate_time);
}
}
WEBVIEW_API int webview_tray_create(struct webview_tray *t) {
struct webview_tray_gtk *priv = (struct webview_tray_gtk *)
calloc(1, sizeof(*priv));
if (!priv) return -1;
t->_priv = priv;
/* Create status icon */
if (t->icon_path && strlen(t->icon_path) > 0) {
priv->icon = gtk_status_icon_new_from_file(t->icon_path);
} else {
priv->icon = gtk_status_icon_new_from_stock(GTK_STOCK_INFO);
}
if (t->tooltip) {
gtk_status_icon_set_tooltip_text(priv->icon, t->tooltip);
}
gtk_status_icon_set_visible(priv->icon, TRUE);
/* Build context menu */
priv->menu = tray_gtk_build_menu(t, t->items, t->item_count);
/* Connect signals */
g_signal_connect(G_OBJECT(priv->icon), "activate",
G_CALLBACK(tray_gtk_activate), t);
g_signal_connect(G_OBJECT(priv->icon), "popup-menu",
G_CALLBACK(tray_gtk_popup), t);
return 0;
}
WEBVIEW_API void webview_tray_update(struct webview_tray *t) {
if (!t || !t->_priv) return;
struct webview_tray_gtk *priv = (struct webview_tray_gtk *)t->_priv;
/* Update icon */
if (t->icon_path && strlen(t->icon_path) > 0) {
gtk_status_icon_set_from_file(priv->icon, t->icon_path);
}
/* Update tooltip */
if (t->tooltip) {
gtk_status_icon_set_tooltip_text(priv->icon, t->tooltip);
}
/* Rebuild menu */
if (priv->menu) {
gtk_widget_destroy(priv->menu);
}
priv->menu = tray_gtk_build_menu(t, t->items, t->item_count);
}
WEBVIEW_API void webview_tray_destroy(struct webview_tray *t) {
if (!t || !t->_priv) return;
struct webview_tray_gtk *priv = (struct webview_tray_gtk *)t->_priv;
if (priv->menu) {
gtk_widget_destroy(priv->menu);
}
if (priv->icon) {
gtk_status_icon_set_visible(priv->icon, FALSE);
g_object_unref(priv->icon);
}
free(priv);
t->_priv = NULL;
}
( run in 1.104 second using v1.01-cache-2.11-cpan-364913b4093 )