/* $Id$ */ /* Copyright (c) 2011-2022 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Panel */ /* 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, version 3 of the License. * * 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, see . */ #include #include #include #include #include #include #if GTK_CHECK_VERSION(3, 0, 0) # include #endif #include "window.h" #include "../config.h" #define _(string) gettext(string) #define N_(string) string /* constants */ #ifndef PREFIX # define PREFIX "/usr/local" #endif #ifndef LIBDIR # define LIBDIR PREFIX "/lib" #endif /* PanelWindow */ /* private */ /* types */ struct _PanelApplet { Plugin * plugin; PanelAppletDefinition * pad; PanelApplet * pa; GtkWidget * widget; }; struct _PanelWindow { PanelWindowType type; PanelWindowPosition position; GtkIconSize iconsize; gint height; GdkRectangle root; /* applets */ PanelAppletHelper * helper; PanelApplet * applets; size_t applets_cnt; /* widgets */ GtkWidget * window; GtkWidget * box; }; /* prototypes */ static void _panel_window_reset(PanelWindow * panel); static void _panel_window_reset_strut(PanelWindow * panel); /* callbacks */ static gboolean _panel_window_on_closex(gpointer data); static gboolean _panel_window_on_configure_event(GtkWidget * widget, GdkEvent * event, gpointer data); /* public */ /* functions */ /* panel_window_new */ PanelWindow * panel_window_new(PanelAppletHelper * helper, PanelWindowType type, PanelWindowPosition position, GtkIconSize iconsize, GdkRectangle * root) { PanelWindow * panel; int icon_width; int icon_height; GtkOrientation orientation; if(gtk_icon_size_lookup(iconsize, &icon_width, &icon_height) != TRUE) { error_set_code(1, _("Invalid panel size")); return NULL; } if((panel = object_new(sizeof(*panel))) == NULL) return NULL; panel->type = type; panel->position = position; panel->iconsize = iconsize; panel->helper = helper; panel->applets = NULL; panel->applets_cnt = 0; if(position != PANEL_WINDOW_POSITION_EMBEDDED) { panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #if GTK_CHECK_VERSION(3, 0, 0) && !GTK_CHECK_VERSION(3, 14, 0) gtk_window_set_has_resize_grip(GTK_WINDOW(panel->window), FALSE); #endif } else { panel->window = gtk_plug_new(0); gtk_widget_show(panel->window); } gtk_container_set_border_width(GTK_CONTAINER(panel->window), 2); panel->height = icon_height + (PANEL_BORDER_WIDTH * 4); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %u height=%d\n", __func__, position, panel->height); #endif panel->box = NULL; orientation = panel_window_get_orientation(panel); #if GTK_CHECK_VERSION(3, 0, 0) panel->box = gtk_box_new(orientation, 2); #else panel->box = (orientation == GTK_ORIENTATION_HORIZONTAL) ? gtk_hbox_new(FALSE, 2) : gtk_vbox_new(FALSE, 2); #endif switch(position) { case PANEL_WINDOW_POSITION_TOP: case PANEL_WINDOW_POSITION_BOTTOM: #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_focus_on_map(GTK_WINDOW(panel->window), FALSE); #endif gtk_window_set_type_hint(GTK_WINDOW(panel->window), GDK_WINDOW_TYPE_HINT_DOCK); gtk_window_stick(GTK_WINDOW(panel->window)); g_signal_connect(panel->window, "configure-event", G_CALLBACK(_panel_window_on_configure_event), panel); break; case PANEL_WINDOW_POSITION_LEFT: case PANEL_WINDOW_POSITION_RIGHT: #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_focus_on_map(GTK_WINDOW(panel->window), FALSE); #endif gtk_window_set_type_hint(GTK_WINDOW(panel->window), GDK_WINDOW_TYPE_HINT_DOCK); gtk_window_stick(GTK_WINDOW(panel->window)); g_signal_connect(panel->window, "configure-event", G_CALLBACK(_panel_window_on_configure_event), panel); break; case PANEL_WINDOW_POSITION_CENTER: gtk_window_set_position(GTK_WINDOW(panel->window), GTK_WIN_POS_CENTER_ALWAYS); gtk_window_stick(GTK_WINDOW(panel->window)); /* fallthrough */ case PANEL_WINDOW_POSITION_FLOATING: gtk_window_set_accept_focus(GTK_WINDOW(panel->window), FALSE); gtk_window_set_decorated(GTK_WINDOW(panel->window), FALSE); case PANEL_WINDOW_POSITION_EMBEDDED: case PANEL_WINDOW_POSITION_MANAGED: break; } g_signal_connect_swapped(panel->window, "delete-event", G_CALLBACK( _panel_window_on_closex), panel); gtk_container_add(GTK_CONTAINER(panel->window), panel->box); gtk_widget_show_all(panel->box); panel_window_reset(panel, root); return panel; } /* panel_window_delete */ void panel_window_delete(PanelWindow * panel) { panel_window_remove_all(panel); gtk_widget_destroy(panel->window); object_delete(panel); } /* accessors */ /* panel_window_get_height */ int panel_window_get_height(PanelWindow * panel) { return panel->height; } /* panel_window_get_icon_size */ GtkIconSize panel_window_get_icon_size(PanelWindow * panel) { return panel->iconsize; } /* panel_window_get_orientation */ GtkOrientation panel_window_get_orientation(PanelWindow * panel) { #if GTK_CHECK_VERSION(2, 16, 0) if(panel->box != NULL) return gtk_orientable_get_orientation(GTK_ORIENTABLE( panel->box)); #endif switch(panel->position) { case PANEL_WINDOW_POSITION_LEFT: case PANEL_WINDOW_POSITION_RIGHT: return GTK_ORIENTATION_VERTICAL; case PANEL_WINDOW_POSITION_BOTTOM: case PANEL_WINDOW_POSITION_CENTER: case PANEL_WINDOW_POSITION_FLOATING: case PANEL_WINDOW_POSITION_MANAGED: case PANEL_WINDOW_POSITION_TOP: default: return GTK_ORIENTATION_HORIZONTAL; } } /* panel_window_get_position */ void panel_window_get_position(PanelWindow * panel, gint * x, gint * y) { GdkWindow * window; window = gtk_widget_get_window(panel->window); gdk_window_get_position(window, x, y); } /* panel_window_get_size */ void panel_window_get_size(PanelWindow * panel, gint * width, gint * height) { gtk_window_get_size(GTK_WINDOW(panel->window), width, height); } /* panel_window_get_type */ PanelWindowType panel_window_get_type(PanelWindow * panel) { return panel->type; } /* panel_window_get_width */ int panel_window_get_width(PanelWindow * panel) { gint width; gtk_window_get_size(GTK_WINDOW(panel->window), &width, NULL); return width; } /* panel_window_get_xid */ uint32_t panel_window_get_xid(PanelWindow * panel) { return (panel->position == PANEL_WINDOW_POSITION_EMBEDDED) ? gtk_plug_get_id(GTK_PLUG(panel->window)) : 0; } /* panel_window_set_accept_focus */ void panel_window_set_accept_focus(PanelWindow * panel, gboolean accept) { gtk_window_set_accept_focus(GTK_WINDOW(panel->window), accept); } /* panel_window_set_keep_above */ void panel_window_set_keep_above(PanelWindow * panel, gboolean keep) { gtk_window_set_keep_above(GTK_WINDOW(panel->window), keep); } /* panel_window_set_title */ void panel_window_set_title(PanelWindow * panel, char const * title) { gtk_window_set_title(GTK_WINDOW(panel->window), title); } /* useful */ /* panel_window_append */ int panel_window_append(PanelWindow * panel, char const * applet) { PanelAppletHelper * helper = panel->helper; PanelApplet * pa; if((pa = realloc(panel->applets, sizeof(*pa) * (panel->applets_cnt + 1))) == NULL) return -error_set_code(1, "%s", strerror(errno)); panel->applets = pa; pa = &panel->applets[panel->applets_cnt]; if((pa->plugin = plugin_new(LIBDIR, PACKAGE, "applets", applet)) == NULL) return -1; pa->widget = NULL; if((pa->pad = plugin_lookup(pa->plugin, "applet")) == NULL || (pa->pa = pa->pad->init(helper, &pa->widget)) == NULL || pa->widget == NULL) { if(pa->pa != NULL) pa->pad->destroy(pa->pa); plugin_delete(pa->plugin); return -1; } gtk_box_pack_start(GTK_BOX(panel->box), pa->widget, pa->pad->expand, pa->pad->fill, 0); gtk_widget_show_all(pa->widget); panel->applets_cnt++; return 0; } /* panel_window_remove_all */ void panel_window_remove_all(PanelWindow * panel) { size_t i; PanelApplet * pa; for(i = 0; i < panel->applets_cnt; i++) { pa = &panel->applets[i]; pa->pad->destroy(pa->pa); plugin_delete(pa->plugin); } free(panel->applets); panel->applets = NULL; panel->applets_cnt = 0; } /* panel_window_reset */ void panel_window_reset(PanelWindow * panel, GdkRectangle * root) { memcpy(&panel->root, root, sizeof(*root)); _panel_window_reset(panel); } /* panel_window_show */ void panel_window_show(PanelWindow * panel, gboolean show) { if(show) gtk_widget_show(panel->window); else gtk_widget_hide(panel->window); } /* private */ /* functions */ /* panel_window_reset */ static void _panel_window_reset(PanelWindow * panel) { switch(panel->position) { case PANEL_WINDOW_POSITION_TOP: gtk_window_move(GTK_WINDOW(panel->window), panel->root.x, 0); gtk_window_resize(GTK_WINDOW(panel->window), panel->root.width, panel->height); break; case PANEL_WINDOW_POSITION_BOTTOM: gtk_window_move(GTK_WINDOW(panel->window), panel->root.x, panel->root.y + panel->root.height - panel->height); gtk_window_resize(GTK_WINDOW(panel->window), panel->root.width, panel->height); break; case PANEL_WINDOW_POSITION_LEFT: /* FIXME really implement */ gtk_window_move(GTK_WINDOW(panel->window), panel->root.x, 0); gtk_window_resize(GTK_WINDOW(panel->window), 48, panel->root.height); break; case PANEL_WINDOW_POSITION_RIGHT: /* FIXME really implement */ gtk_window_move(GTK_WINDOW(panel->window), panel->root.x + panel->root.width - 48, panel->root.y); gtk_window_resize(GTK_WINDOW(panel->window), 48, panel->root.height); break; case PANEL_WINDOW_POSITION_CENTER: case PANEL_WINDOW_POSITION_FLOATING: case PANEL_WINDOW_POSITION_MANAGED: case PANEL_WINDOW_POSITION_EMBEDDED: break; } } /* panel_window_reset_strut */ static void _panel_window_reset_strut(PanelWindow * panel) { GdkWindow * window; GdkAtom atom; GdkAtom cardinal; unsigned long strut[12]; #if GTK_CHECK_VERSION(2, 14, 0) window = gtk_widget_get_window(panel->window); #else window = panel->window->window; #endif memset(&strut, 0, sizeof(strut)); /* FIXME check that this code is correct */ switch(panel->position) { case PANEL_WINDOW_POSITION_TOP: strut[2] = panel->height; strut[8] = panel->root.x; strut[9] = panel->root.x + panel->root.width; break; case PANEL_WINDOW_POSITION_BOTTOM: strut[3] = panel->height; strut[10] = panel->root.x; strut[11] = panel->root.x + panel->root.width; break; #if 0 /* FIXME implement */ case PANEL_WINDOW_POSITION_LEFT: case PANEL_WINDOW_POSITION_RIGHT: break; #endif case PANEL_WINDOW_POSITION_CENTER: case PANEL_WINDOW_POSITION_FLOATING: case PANEL_WINDOW_POSITION_MANAGED: case PANEL_WINDOW_POSITION_EMBEDDED: break; } cardinal = gdk_atom_intern("CARDINAL", FALSE); atom = gdk_atom_intern("_NET_WM_STRUT", FALSE); gdk_property_change(window, atom, cardinal, 32, GDK_PROP_MODE_REPLACE, (guchar *)strut, 4); atom = gdk_atom_intern("_NET_WM_STRUT_PARTIAL", FALSE); gdk_property_change(window, atom, cardinal, 32, GDK_PROP_MODE_REPLACE, (guchar *)strut, 12); } /* callbacks */ /* panel_window_on_closex */ static gboolean _panel_window_on_closex(gpointer data) { PanelWindow * panel = data; panel_window_show(panel, FALSE); gtk_main_quit(); return TRUE; } /* panel_window_on_configure_event */ static gboolean _panel_window_on_configure_event(GtkWidget * widget, GdkEvent * event, gpointer data) { PanelWindow * panel = data; GdkEventConfigure * cevent = &event->configure; (void) widget; if(event->type != GDK_CONFIGURE) return FALSE; panel->height = cevent->height; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %u height=%d\n", __func__, panel->position, panel->height); #endif /* move to the proper position again if necessary */ switch(panel->position) { case PANEL_WINDOW_POSITION_TOP: if(cevent->y != panel->root.y) _panel_window_reset(panel); else _panel_window_reset_strut(panel); break; case PANEL_WINDOW_POSITION_BOTTOM: if(cevent->y + cevent->height != panel->root.height) _panel_window_reset(panel); else _panel_window_reset_strut(panel); break; default: _panel_window_reset_strut(panel); break; } return FALSE; }