Last active
April 23, 2021 13:17
-
-
Save Misko-2083/28b4affa9a7802f2eab108cced383dcb to your computer and use it in GitHub Desktop.
popover attempt as a xfce4-panel applet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright © 2021 misko_2083 | |
* | |
* Distributed under terms of the GPL2 license. | |
* | |
* compile | |
* gcc -Wall -s -shared -fPIC -g desktop-icons-applet.c -o libdicons.so $(pkg-config --libs --cflags gtk+-3.0 libxfce4panel-2.0 libxfconf-0) | |
* move to libdir (debian 64bit) | |
* mv libdicons.so /usr/lib/x86_64-linux-gnu/xfce4/panel/plugins/libdicons.so | |
*/ | |
#include <libxfce4util/libxfce4util.h> | |
#include <libxfce4panel/xfce-panel-plugin.h> | |
#include <xfconf/xfconf.h> | |
#define DEFAULT_ICON_NAME "emblem-desktop" | |
#define DEFAULT_TOOLTIP_MESSAGE "Show/Hide Desktop Icons" | |
#define DEFAULT_TITLE "dicons" | |
#define XFCE_PLUGIN_VERSION "0.3" | |
typedef struct _DiconsPlugin { | |
XfcePanelPlugin *plugin; | |
GtkWidget *button; | |
GtkWidget *cancel_button; | |
GtkWidget *icon; | |
GtkWidget *window; | |
GtkWidget *popover; | |
GtkStyleProvider *style_provider; | |
gchar *icon_name; | |
} DiconsPlugin; | |
struct icon { | |
char home[60]; | |
char trash[60]; | |
char filesystem[60]; | |
char removable[60]; | |
}; | |
static const struct icon icon = { | |
.home = "/desktop-icons/file-icons/show-home", | |
.trash = "/desktop-icons/file-icons/show-trash", | |
.filesystem = "/desktop-icons/file-icons/show-filesystem", | |
.removable = "/desktop-icons/file-icons/show-removable" | |
}; | |
static void | |
button_clicked (GtkWidget *button, | |
DiconsPlugin *dicons); | |
static gboolean | |
on_popup_focus_out (GtkWidget *widget, | |
GdkEventFocus *event, | |
gpointer data); | |
static gboolean | |
on_key_pressed (GtkWidget *widget, | |
GdkEventKey *event, | |
gpointer data); | |
static const char dicons_plugin_copyright[] = | |
"Copyright \xc2\xa9 2020 Miloš Pavlović\n"; | |
static void dicons_about(XfcePanelPlugin *plugin) | |
{ | |
const gchar *auth[] = { "Miloš Pavlović", NULL }; | |
GdkPixbuf *icon; | |
icon = xfce_panel_pixbuf_from_source("emblem-desktop", NULL, 32); | |
gtk_show_about_dialog(NULL, | |
"logo", icon, | |
"license", xfce_get_license_text(XFCE_LICENSE_TEXT_GPL), | |
"version", XFCE_PLUGIN_VERSION, | |
"program-name", "dicons-applet", | |
"comments", _("Opens a configuration menu for desktop icons"), | |
"website", "https://github.com/Misko-2083", | |
"copyright", _(dicons_plugin_copyright), | |
"authors", auth, | |
NULL); | |
if (icon) | |
g_object_unref(G_OBJECT(icon)); | |
} | |
static void | |
_quit_cb (GtkWidget *button, GtkWidget *window, gpointer data) | |
{ | |
(void)data; /* Avoid compiler warnings */ | |
gtk_widget_hide (window); | |
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); | |
return; | |
} | |
static gboolean | |
on_popup_focus_out (GtkWidget *widget, | |
GdkEventFocus *event, | |
gpointer data) | |
{ | |
gtk_widget_hide (widget); | |
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), FALSE); | |
return TRUE; | |
} | |
static gboolean | |
on_key_pressed (GtkWidget *widget, | |
GdkEventKey *event, | |
gpointer data) | |
{ | |
if (event->keyval == GDK_KEY_Escape){ | |
gtk_widget_hide (widget); | |
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), FALSE); | |
return TRUE; | |
} | |
return FALSE; | |
} | |
static gboolean | |
on_switch (GtkWidget *widget, | |
gboolean *state, | |
gpointer user_data) | |
{ | |
XfconfChannel *channel; | |
xfconf_init(NULL); | |
channel = xfconf_channel_get("xfce4-desktop"); | |
if (state) | |
xfconf_channel_set_bool(channel, user_data, TRUE); | |
else | |
xfconf_channel_set_bool(channel, user_data, FALSE); | |
xfconf_shutdown(); | |
return FALSE; | |
} | |
static gboolean dicons_size_changed (XfcePanelPlugin *plugin, | |
gint size, | |
DiconsPlugin *dicons) | |
{ | |
size = size / xfce_panel_plugin_get_nrows(plugin); | |
gtk_widget_set_size_request (GTK_WIDGET (plugin), size, size); | |
return TRUE; | |
} | |
static void button_clicked(GtkWidget *button, | |
DiconsPlugin *dicons) | |
{ | |
gint x, y; | |
gint wx, wy, offset_x, offset_y, win_x, win_y; | |
XfceScreenPosition position; | |
cairo_rectangle_int_t rect; | |
gint width, height; | |
GdkDisplay *display = gdk_display_get_default(); | |
GdkSeat *seat = gdk_display_get_default_seat(display); | |
GdkDevice *device = gdk_seat_get_pointer(seat); | |
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dicons->button))) | |
{ | |
xfce_panel_plugin_block_autohide(dicons->plugin, TRUE); | |
gtk_widget_get_allocation(dicons->button, &rect); | |
if (GTK_IS_TOGGLE_BUTTON (button)) { | |
gtk_widget_show_all(GTK_WIDGET(dicons->window)); | |
xfce_panel_plugin_position_widget (dicons->plugin, | |
dicons->window, | |
NULL, | |
&x, | |
&y); | |
} else { | |
gdk_window_get_device_position(gdk_get_default_root_window(), | |
device, &x, &y, NULL); | |
} | |
position = xfce_panel_plugin_get_screen_position(dicons->plugin); | |
offset_x = 0; | |
offset_y = 0; | |
gint root_x, root_y, win_width, win_height; | |
gtk_window_get_size(GTK_WINDOW(dicons->window), &win_width, &win_height); | |
gtk_widget_realize (dicons->window); | |
GdkRectangle workarea = {0}; | |
gdk_monitor_get_workarea( | |
gdk_display_get_primary_monitor(gdk_display_get_default()), &workarea); | |
gtk_window_get_position (GTK_WINDOW(dicons->window), &win_x, &win_y); | |
GtkWidget *toplevel = gtk_widget_get_toplevel (dicons->button); | |
gtk_widget_translate_coordinates(dicons->button, toplevel, 0, 0, &wx, &wy); | |
if (gtk_widget_is_toplevel (toplevel)) | |
{ | |
gtk_window_get_size(GTK_WINDOW(toplevel), &width, &height); | |
gtk_window_get_position (GTK_WINDOW(toplevel), | |
&root_x, | |
&root_y); | |
} | |
if (xfce_screen_position_is_horizontal(position)) { | |
/* horizontal */ | |
if (win_width/2 > (root_x + wx)){ | |
offset_x = x + 10; | |
rect.x = wx + 10; | |
} | |
else if ((win_width) > (workarea.width - (root_x + wx))) { | |
offset_x = - 10; | |
rect.x = (win_width - (workarea.width - (root_x + wx + offset_x))); | |
if ((workarea.width - (root_x + wx)) > win_width/2) { | |
offset_x = workarea.width - (root_x + wx) - win_width/2 - rect.width/2; | |
rect.x = win_width/2 - rect.width/2; | |
} | |
} | |
else { | |
offset_x = win_width/2 - rect.width/2; | |
rect.x = win_width/2 - rect.width/2; | |
} | |
if (win_y > (root_y + wy)) { | |
/* top panel position */ | |
rect.y = 0; | |
} | |
if (win_y < (root_y + wy)) { | |
/* bottom */ | |
rect.y = win_height + rect.height; | |
} | |
} else { | |
if (win_height/2 > (root_y + wy)){ | |
offset_y = y + 10; | |
rect.y = wy + 10; | |
} | |
else if (win_height > (workarea.height - (root_y + wy))) { | |
offset_y = - 10; | |
rect.y = (win_height - (workarea.height - (root_y + wy + offset_y))); | |
if ((workarea.height - (root_y + wy)) > win_height/2) { | |
offset_y = workarea.height - (root_y + wy) - win_height/2 - rect.height/2; | |
rect.y = win_height/2 - rect.height/2; | |
} | |
} | |
else { | |
offset_y = win_height/2 - rect.height/2; | |
rect.y = win_height/2 - rect.height/2; | |
} | |
/* vertical */ | |
if (win_x < (root_x + wx)) { | |
/* left */ | |
rect.x = 0; | |
} | |
if (win_x < (root_x + wx)) { | |
/* right */ | |
rect.x = win_width + rect.width; | |
} | |
} | |
gtk_window_move (GTK_WINDOW(dicons->window), x - offset_x, y - offset_y); | |
gtk_popover_set_pointing_to(GTK_POPOVER(dicons->popover), &rect); | |
gtk_popover_popup(GTK_POPOVER(dicons->popover)); | |
/* give focus to at least one widget, to avoid drawing the window on some themes */ | |
gtk_widget_set_sensitive (dicons->cancel_button, TRUE); | |
gtk_widget_grab_focus(dicons->cancel_button); | |
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dicons->button), TRUE); | |
} else { | |
_quit_cb(dicons->button, dicons->window, NULL); | |
if (GTK_IS_TOGGLE_BUTTON (button)) | |
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); | |
xfce_panel_plugin_block_autohide(dicons->plugin, FALSE); | |
gtk_popover_popdown(GTK_POPOVER(dicons->popover)); | |
} | |
} | |
static void popover_size_changed (DiconsPlugin *dicons) | |
{ | |
/*here react to size change*/ | |
g_print("size changed\n"); | |
} | |
static DiconsPlugin *dicons_init(XfcePanelPlugin *plugin) | |
{ | |
GtkWidget *box; | |
GtkWidget *box_a; | |
GtkWidget *box_b; | |
GtkWidget *boxl; | |
GtkWidget *scrolled_window; | |
GtkWidget *about_button; | |
GtkWidget *question; | |
GtkWidget *label_home; | |
GtkWidget *label_trash; | |
GtkWidget *label_filesystem; | |
GtkWidget *label_removable; | |
GtkWidget *image; | |
GtkWidget *switch_home; | |
GtkWidget *switch_trash; | |
GtkWidget *switch_filesystem; | |
GtkWidget *switch_removable; | |
XfconfChannel *channel; | |
DiconsPlugin *dicons = g_slice_new0(DiconsPlugin); | |
dicons->plugin = plugin; | |
dicons->icon_name = g_strdup(DEFAULT_ICON_NAME); | |
dicons->button = xfce_panel_create_toggle_button(); | |
xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (dicons->plugin), | |
dicons->button); | |
gtk_init(NULL, NULL); | |
dicons->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); | |
gtk_window_set_type_hint (GTK_WINDOW(dicons->window), | |
GDK_WINDOW_TYPE_HINT_POPUP_MENU); | |
gtk_widget_set_size_request(dicons->window, 400, 250); | |
gtk_window_set_resizable (GTK_WINDOW(dicons->window), TRUE); | |
gtk_window_set_keep_above (GTK_WINDOW (dicons->window), TRUE); | |
gtk_window_stick (GTK_WINDOW (dicons->window)); | |
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dicons->window), TRUE); | |
gtk_window_set_title (GTK_WINDOW (dicons->window), "Desktop Icons"); | |
gtk_widget_set_events (dicons->window, GDK_FOCUS_CHANGE_MASK); | |
g_signal_connect (G_OBJECT (GTK_WINDOW (dicons->window)), | |
"focus-out-event", | |
G_CALLBACK (on_popup_focus_out), | |
dicons->button); | |
gtk_widget_set_events (GTK_WIDGET(dicons->window), GDK_KEY_PRESS_MASK); | |
g_signal_connect (G_OBJECT (GTK_WINDOW (dicons->window)), | |
"key-press-event", | |
G_CALLBACK (on_key_pressed), | |
dicons->button); | |
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); | |
box_a = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); | |
box_b = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); | |
boxl = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); | |
gtk_box_set_homogeneous (GTK_BOX (box), TRUE); | |
image = gtk_image_new_from_icon_name (dicons->icon_name, | |
GTK_ICON_SIZE_DND); | |
question = gtk_label_new ("Desktop Icons"); | |
label_home = gtk_label_new ("Home"); | |
label_trash = gtk_label_new ("Trash"); | |
label_filesystem = gtk_label_new ("Filesystem"); | |
label_removable = gtk_label_new ("Removable"); | |
switch_home = gtk_switch_new (); | |
switch_trash = gtk_switch_new (); | |
switch_filesystem = gtk_switch_new (); | |
switch_removable = gtk_switch_new (); | |
g_signal_connect (G_OBJECT (switch_home), | |
"state-set", | |
G_CALLBACK (on_switch), | |
(void *)icon.home); | |
g_signal_connect (G_OBJECT (switch_trash), | |
"state-set", | |
G_CALLBACK (on_switch), | |
(void *)icon.trash); | |
g_signal_connect (G_OBJECT (switch_filesystem), | |
"state-set", | |
G_CALLBACK (on_switch), | |
(void *)icon.filesystem); | |
g_signal_connect (G_OBJECT (switch_removable), | |
"state-set", | |
G_CALLBACK (on_switch), | |
(void *)icon.removable); | |
xfconf_init(NULL); | |
channel = xfconf_channel_get("xfce4-desktop"); | |
/* set initial switches */ | |
if (xfconf_channel_get_bool(channel, icon.home, TRUE)) | |
gtk_switch_set_state (GTK_SWITCH(switch_home), TRUE); | |
else | |
gtk_switch_set_state (GTK_SWITCH(switch_home), FALSE); | |
if (xfconf_channel_get_bool(channel, icon.trash, TRUE)) | |
gtk_switch_set_state (GTK_SWITCH(switch_trash), TRUE); | |
else | |
gtk_switch_set_state (GTK_SWITCH(switch_trash), FALSE); | |
if (xfconf_channel_get_bool(channel, icon.filesystem, TRUE)) | |
gtk_switch_set_state (GTK_SWITCH(switch_filesystem), TRUE); | |
else | |
gtk_switch_set_state (GTK_SWITCH(switch_filesystem), FALSE); | |
if (xfconf_channel_get_bool(channel, icon.removable, TRUE)) | |
gtk_switch_set_state (GTK_SWITCH(switch_removable), TRUE); | |
else | |
gtk_switch_set_state (GTK_SWITCH(switch_removable), FALSE); | |
xfconf_shutdown(); | |
dicons->popover = gtk_popover_new(dicons->window); | |
gtk_popover_set_constrain_to(GTK_POPOVER(dicons->popover), | |
GTK_POPOVER_CONSTRAINT_NONE); | |
/* modal blocks the panel preferences process, | |
* it needs to be set to FALSE */ | |
gtk_popover_set_modal(GTK_POPOVER(dicons->popover), FALSE); | |
dicons->style_provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ()); | |
gtk_style_context_add_provider (gtk_widget_get_style_context (dicons->popover), | |
dicons->style_provider, | |
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); | |
char *css_text; | |
css_text = g_strdup_printf ("popover {\n" | |
" padding: 0px;\n" | |
" border: solid 2px;\n" | |
"}\n"); | |
gtk_css_provider_load_from_data (GTK_CSS_PROVIDER | |
(dicons->style_provider), | |
css_text, -1, NULL); | |
g_free (css_text); | |
gtk_container_add(GTK_CONTAINER(dicons->window), dicons->popover); | |
gtk_container_set_border_width (GTK_CONTAINER(dicons->window), | |
0); | |
gtk_container_set_border_width (GTK_CONTAINER(dicons->popover), | |
10); | |
scrolled_window = gtk_scrolled_window_new(NULL, NULL); | |
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scrolled_window), | |
GTK_SHADOW_ETCHED_OUT); | |
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | |
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); | |
gtk_widget_set_size_request(scrolled_window, 400, 250); | |
gtk_container_add(GTK_CONTAINER(scrolled_window), boxl); | |
gtk_container_add(GTK_CONTAINER(dicons->popover), scrolled_window); | |
gtk_box_pack_start (GTK_BOX(box_a), label_home, TRUE, FALSE, 10); | |
gtk_box_pack_start (GTK_BOX(box_b), switch_home, TRUE, FALSE, 10); | |
gtk_box_pack_start (GTK_BOX(box_a), label_trash, TRUE, FALSE, 10); | |
gtk_box_pack_start (GTK_BOX(box_b), switch_trash, TRUE, FALSE, 10); | |
gtk_box_pack_start (GTK_BOX(box_a), label_filesystem, TRUE, FALSE, 10); | |
gtk_box_pack_start (GTK_BOX(box_b), switch_filesystem, TRUE, FALSE, 10); | |
gtk_box_pack_start (GTK_BOX(box_a), label_removable, TRUE, FALSE, 10); | |
gtk_box_pack_start (GTK_BOX(box_b), switch_removable, TRUE, FALSE, 10); | |
about_button = gtk_button_new_with_label("About"); | |
dicons->cancel_button = gtk_button_new_with_label("Cancel"); | |
g_signal_connect(G_OBJECT(dicons->cancel_button), | |
"clicked", | |
G_CALLBACK(_quit_cb), | |
dicons->window); | |
g_signal_connect(G_OBJECT(about_button), | |
"clicked", | |
G_CALLBACK(dicons_about), | |
plugin); | |
gtk_box_pack_start (GTK_BOX(boxl), image, TRUE, TRUE, 0); | |
gtk_box_pack_start (GTK_BOX(boxl), question, TRUE, TRUE, 0); | |
gtk_box_pack_start (GTK_BOX(boxl), box_a, FALSE, FALSE, 5); | |
gtk_box_pack_start (GTK_BOX(boxl), box_b, FALSE, FALSE, 5); | |
gtk_box_pack_start (GTK_BOX(box), about_button, TRUE, TRUE, 10); | |
gtk_box_pack_start (GTK_BOX (box), dicons->cancel_button, TRUE, TRUE, 10); | |
gtk_box_pack_start (GTK_BOX(boxl), box, FALSE, FALSE, 10); | |
gtk_window_set_decorated (GTK_WINDOW(dicons->window), FALSE); | |
gtk_widget_set_sensitive(scrolled_window, TRUE); | |
gtk_widget_show(dicons->button); | |
g_signal_connect (G_OBJECT(dicons->button), "toggled", | |
G_CALLBACK(button_clicked), dicons); | |
g_signal_connect (dicons->popover, "size-allocate", | |
G_CALLBACK (popover_size_changed), | |
dicons); | |
gtk_widget_set_tooltip_text (GTK_WIDGET(dicons->button), | |
DEFAULT_TOOLTIP_MESSAGE); | |
dicons->icon = xfce_panel_image_new_from_source(dicons->icon_name); | |
gtk_widget_show(dicons->icon); | |
gtk_container_add(GTK_CONTAINER(dicons->button), dicons->icon); | |
return dicons; | |
} | |
static void dicons_free(XfcePanelPlugin *plugin, DiconsPlugin *dicons) | |
{ | |
gtk_widget_destroy(dicons->button); | |
gtk_widget_destroy(dicons->icon); | |
if (GTK_IS_WIDGET (dicons->window)) | |
gtk_widget_destroy(dicons->window); | |
if (GTK_IS_WIDGET (dicons->popover)) | |
gtk_widget_destroy(dicons->popover); | |
g_slice_free(DiconsPlugin, dicons); | |
} | |
static void set_button_active (GtkToggleButton *button) | |
{ | |
if (GTK_IS_TOGGLE_BUTTON(button)) { | |
if (!gtk_toggle_button_get_active(button)) { | |
gtk_toggle_button_set_active(button, TRUE); | |
} | |
else | |
{ | |
gtk_toggle_button_set_active(button, FALSE); | |
} | |
} | |
} | |
static gboolean dicons_remote (XfcePanelPlugin *plugin, | |
gchar *name, | |
GValue *value, | |
DiconsPlugin *dicons) | |
{ | |
g_return_val_if_fail (value == NULL || G_IS_VALUE (value), FALSE); | |
if (strcmp (name, "popup") == 0 | |
&& !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dicons->button)) | |
&& gtk_widget_get_visible (GTK_WIDGET (plugin)) ) | |
{ | |
if (value != NULL | |
&& G_VALUE_HOLDS_BOOLEAN (value) | |
&& g_value_get_boolean (value)) | |
{ | |
set_button_active(GTK_TOGGLE_BUTTON(dicons->button)); | |
/* popup here at mouse pointer , where X is an internal id | |
* xfce4-panel --plugin-event=desktop-icons-applet-X:popup:bool:true | |
*/ | |
button_clicked(NULL, dicons); | |
} | |
else | |
{ | |
set_button_active(GTK_TOGGLE_BUTTON(dicons->button)); | |
/* popup here, where X is an internal id | |
* xfce4-panel --plugin-event=desktop-icons-applet-X:popup:bool:false | |
*/ | |
button_clicked(dicons->button, dicons); | |
} | |
return TRUE; | |
} | |
return FALSE; | |
} | |
static void dicons_construct(XfcePanelPlugin *plugin) | |
{ | |
DiconsPlugin *dicons; | |
dicons = dicons_init(plugin); | |
gtk_container_add(GTK_CONTAINER(plugin), dicons->button); | |
xfce_panel_plugin_add_action_widget(plugin, dicons->button); | |
xfce_panel_plugin_menu_show_about(plugin); | |
g_signal_connect (G_OBJECT(plugin), | |
"free-data", | |
G_CALLBACK(dicons_free), dicons); | |
g_signal_connect (G_OBJECT(plugin), | |
"size-changed", | |
G_CALLBACK(dicons_size_changed), dicons); | |
g_signal_connect (G_OBJECT (plugin), | |
"remote-event", | |
G_CALLBACK(dicons_remote), dicons); | |
g_signal_connect (G_OBJECT (plugin), | |
"about", | |
G_CALLBACK (dicons_about), dicons); | |
} | |
XFCE_PANEL_PLUGIN_REGISTER(dicons_construct); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment