Skip to content

Instantly share code, notes, and snippets.

@Misko-2083
Last active April 23, 2021 13:17
Show Gist options
  • Save Misko-2083/28b4affa9a7802f2eab108cced383dcb to your computer and use it in GitHub Desktop.
Save Misko-2083/28b4affa9a7802f2eab108cced383dcb to your computer and use it in GitHub Desktop.
popover attempt as a xfce4-panel applet
/*
* 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