diff options
Diffstat (limited to 'libmdock/mdock-item.c')
-rw-r--r-- | libmdock/mdock-item.c | 425 |
1 files changed, 411 insertions, 14 deletions
diff --git a/libmdock/mdock-item.c b/libmdock/mdock-item.c index 726644f..43dcdca 100644 --- a/libmdock/mdock-item.c +++ b/libmdock/mdock-item.c @@ -17,40 +17,437 @@ * along with MDock. If not, see <http://www.gnu.org/licenses/>. */ +#include "mdock-enums.h" #include "mdock-item.h" #define MDOCK_ITEM_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), MDOCK_TYPE_ITEM, MDockItemPrivate)) struct _MDockItemPrivate { + MDockItemType type; + GDesktopAppInfo *appinfo; + guint icon_size; + GdkPixbuf *icon; + GList *windows; + + GtkMenu *menu; + GtkImageMenuItem *menu_close_all; + GtkImageMenuItem *menu_pin; + GtkImageMenuItem *menu_launch; }; -G_DEFINE_TYPE (MDockItem, mdock_item, GTK_TYPE_BOX) +G_DEFINE_TYPE (MDockItem, mdock_item, GTK_TYPE_WIDGET) -static void -mdock_item_finalize (GObject *object) +enum { + PROP_0, + PROP_TYPE, + PROP_DESKTOP_APP_INFO, + PROP_ICON_SIZE, + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL }; + +static GdkPixbuf *mdock_item_load_icon(MDockItem *self, guint icon_size) { - G_OBJECT_CLASS (mdock_item_parent_class)->finalize (object); + if (self->priv->appinfo) { + GtkIconTheme *theme = gtk_icon_theme_get_default(); + GIcon *icon = g_app_info_get_icon(G_APP_INFO(self->priv->appinfo)); + if (icon) { + GtkIconInfo *info = gtk_icon_theme_lookup_by_gicon(theme, icon, icon_size, + GTK_ICON_LOOKUP_GENERIC_FALLBACK | GTK_ICON_LOOKUP_FORCE_SIZE); + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(info, NULL); + gtk_icon_info_free(info); + if (pixbuf) { + return pixbuf; + } + } + } + + if (self->priv->windows) { + GdkPixbuf *pixbuf = wnck_window_get_icon(self->priv->windows->data); + if (pixbuf) { + return gdk_pixbuf_scale_simple(pixbuf, self->priv->icon_size, icon_size, GDK_INTERP_BILINEAR); + } + } + + return NULL; } -static void -mdock_item_class_init (MDockItemClass *klass) +static const gchar *mdock_item_get_display_name(MDockItem *self) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); + if (self->priv->appinfo) { + return g_app_info_get_display_name(G_APP_INFO(self->priv->appinfo)); + } else if (self->priv->windows) { + WnckApplication *app = wnck_window_get_application(self->priv->windows->data); + return wnck_application_get_name(app); + } else { + return NULL; + } +} - object_class->finalize = mdock_item_finalize; +static void mdock_item_update_icon(MDockItem *self) +{ + g_clear_object(&self->priv->icon); + self->priv->icon = mdock_item_load_icon(self, self->priv->icon_size); + gtk_widget_queue_draw(GTK_WIDGET(self)); +} - g_type_class_add_private (object_class, sizeof (MDockItemPrivate)); +static void mdock_item_update_tooltip(MDockItem *self) +{ + gtk_widget_set_tooltip_text(GTK_WIDGET(self), mdock_item_get_display_name(self)); } -static void -mdock_item_init (MDockItem *self) +static void mdock_item_update_menu(MDockItem *self) +{ + gtk_widget_set_visible(GTK_WIDGET(self->priv->menu_close_all), self->priv->windows != NULL); + gtk_widget_set_visible(GTK_WIDGET(self->priv->menu_pin), self->priv->appinfo != NULL); + gtk_widget_set_visible(GTK_WIDGET(self->priv->menu_launch), self->priv->appinfo != NULL); + if (!gtk_image_menu_item_get_image(self->priv->menu_launch) && self->priv->icon) { + gint width, height; + if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height)) { + GdkPixbuf *pixbuf = mdock_item_load_icon(self, MIN(width, height)); + if (pixbuf) { + GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf); + gtk_image_menu_item_set_image(self->priv->menu_launch, image); + g_object_unref(pixbuf); + } + } + } +} + +static void mdock_item_realize(GtkWidget *widget) +{ + GdkWindowAttr attributes; + guint attributes_mask; + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y; + + widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask); + + gdk_window_set_user_data(widget->window, widget); + gtk_widget_set_realized(widget, TRUE); + + widget->style = gtk_style_attach(widget->style, widget->window); + gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL); +} + +static void mdock_item_size_request(GtkWidget *widget, GtkRequisition *requisition) +{ + MDockItem *self = MDOCK_ITEM(widget); + requisition->width = requisition->height = self->priv->icon_size; +} + +static void mdock_item_size_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + MDockItem *self = MDOCK_ITEM(widget); + allocation->width = allocation->height = self->priv->icon_size; + GTK_WIDGET_CLASS(mdock_item_parent_class)->size_allocate(widget, allocation); +} + +static gboolean mdock_item_button_press(GtkWidget *widget, GdkEventButton *event) +{ + MDockItem *self = MDOCK_ITEM(widget); + switch (event->button) { + case 1: + if (self->priv->windows) { + // This item has one window at least + if (self->priv->windows->next) { + // There is more than one window + gboolean found_window = FALSE; + for (GList *l = self->priv->windows; l; l = g_list_next(l)) { + WnckWindow *window = WNCK_WINDOW(l->data); + if (wnck_window_is_most_recently_activated(window) + || wnck_window_transient_is_most_recently_activated(window)) { + // This is the currently focused window, let's focus the next in the list + GList *n = l->next ? g_list_next(l) : self->priv->windows; + window = WNCK_WINDOW(n->data); + wnck_window_activate_transient(window, event->time); + found_window = TRUE; + break; + } + } + if (!found_window) { + WnckWindow *window = WNCK_WINDOW(self->priv->windows->data); + wnck_window_activate_transient(window, event->time); + } + } else { + // There is only one window in this group + WnckWindow *window = WNCK_WINDOW(self->priv->windows->data); + if (wnck_window_is_minimized(window)) { + wnck_window_unminimize(window, event->time); + } else if (wnck_window_is_most_recently_activated(window) + || wnck_window_transient_is_most_recently_activated(window)) { + wnck_window_minimize(window); + } else { + wnck_window_activate_transient(window, event->time); + } + } + } else if (self->priv->appinfo) { + // No windows, but we have an application to launch! + GError *error = NULL; + if (!g_app_info_launch(G_APP_INFO(self->priv->appinfo), NULL, NULL, &error)) { + GtkWidget *msg; + + g_warning("Cannot launch '%s': %s", + g_app_info_get_display_name(G_APP_INFO(self->priv->appinfo)), + error->message); + + msg = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + "Cannot launch '%s': %s", + g_app_info_get_display_name(G_APP_INFO(self->priv->appinfo)), + error->message); + + g_signal_connect_swapped(msg, "response", + G_CALLBACK(gtk_widget_destroy), msg); + + gtk_widget_show_all(msg); + g_error_free(error); + } + } else { + g_warning("A MDockItem has no windows and no appinfo to launch"); + } + + break; + + case 3: + gtk_menu_popup(self->priv->menu, + NULL, NULL, NULL, NULL, + event->button, event->time); + break; + } + + return TRUE; +} + +static gboolean mdock_item_expose(GtkWidget *widget, GdkEventExpose *event) +{ + MDockItem *self = MDOCK_ITEM(widget); + cairo_t *cr = gdk_cairo_create(widget->window); + + if (self->priv->icon) { + gdk_cairo_set_source_pixbuf(cr, self->priv->icon, 0, 0); + cairo_paint(cr); + } + + cairo_destroy(cr); + + return FALSE; +} + +static void mdock_item_set_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + MDockItem *self = MDOCK_ITEM(object); + switch (property_id) { + case PROP_TYPE: + self->priv->type = g_value_get_enum(value); + mdock_item_update_icon(self); + break; + case PROP_DESKTOP_APP_INFO: + self->priv->appinfo = g_value_dup_object(value); + mdock_item_update_icon(self); + mdock_item_update_tooltip(self); + break; + case PROP_ICON_SIZE: + self->priv->icon_size = g_value_get_uint(value); + gtk_widget_queue_resize(GTK_WIDGET(self)); + mdock_item_update_icon(self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void mdock_item_get_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + MDockItem *self = MDOCK_ITEM(object); + switch (property_id) { + case PROP_TYPE: + g_value_set_enum(value, self->priv->type); + break; + case PROP_DESKTOP_APP_INFO: + g_value_set_object(value, self->priv->appinfo); + break; + case PROP_ICON_SIZE: + g_value_set_uint(value, self->priv->icon_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void mdock_item_dispose(GObject *object) +{ + MDockItem *self = MDOCK_ITEM(object); + g_clear_object(&self->priv->appinfo); + g_clear_object(&self->priv->icon); + g_clear_object(&self->priv->menu); + g_clear_object(&self->priv->menu_close_all); + g_clear_object(&self->priv->menu_pin); + g_clear_object(&self->priv->menu_launch); + G_OBJECT_CLASS(mdock_item_parent_class)->dispose(object); +} + +static void mdock_item_finalize(GObject *object) +{ + MDockItem *self = MDOCK_ITEM(object); + g_list_free(self->priv->windows); + G_OBJECT_CLASS(mdock_item_parent_class)->finalize(object); +} + +static void mdock_item_init(MDockItem *self) { self->priv = MDOCK_ITEM_GET_PRIVATE (self); + self->priv->type = MDOCK_ITEM_TYPE_APPLICATION; + self->priv->icon_size = 48; + + gtk_widget_add_events(GTK_WIDGET(self), GDK_BUTTON_PRESS_MASK); + + self->priv->menu = g_object_ref_sink(gtk_menu_new()); + + self->priv->menu_close_all = g_object_ref_sink(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, NULL)); + gtk_menu_shell_append(GTK_MENU_SHELL(self->priv->menu), + GTK_WIDGET(self->priv->menu_close_all)); + g_signal_connect_swapped(self->priv->menu_close_all, "activate", + G_CALLBACK(mdock_item_close_all_windows), self); + + self->priv->menu_pin = g_object_ref_sink(gtk_image_menu_item_new_with_mnemonic("_Pin")); + gtk_menu_shell_append(GTK_MENU_SHELL(self->priv->menu), + GTK_WIDGET(self->priv->menu_pin)); + + self->priv->menu_launch = g_object_ref_sink(gtk_image_menu_item_new_with_mnemonic("_New instance")); + gtk_menu_shell_append(GTK_MENU_SHELL(self->priv->menu), + GTK_WIDGET(self->priv->menu_launch)); +} + +static void +mdock_item_class_init(MDockItemClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + obj_class->set_property = mdock_item_set_property; + obj_class->get_property = mdock_item_get_property; + obj_class->dispose = mdock_item_dispose; + obj_class->finalize = mdock_item_finalize; + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + widget_class->realize = mdock_item_realize; + widget_class->size_request = mdock_item_size_request; + widget_class->size_allocate = mdock_item_size_allocate; + widget_class->button_press_event = mdock_item_button_press; + widget_class->expose_event = mdock_item_expose; + + obj_properties[PROP_TYPE] = g_param_spec_enum("type", + "Type of this dock item", + "Set the type of this dock item", + M_TYPE_DOCK_ITEM_TYPE, + MDOCK_ITEM_TYPE_APPLICATION, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_DESKTOP_APP_INFO] = g_param_spec_object("desktop-app-info", + "The GDesktopAppInfo this application or launcher refers to", + "Set the GDesktopAppInfo this application or launcher refers to", + G_TYPE_DESKTOP_APP_INFO, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_ICON_SIZE] = g_param_spec_uint("icon-size", + "Icon size", + "Set the icon size", + 16, 256, + 48, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(obj_class, N_PROPERTIES, obj_properties); + + g_type_class_add_private (obj_class, sizeof (MDockItemPrivate)); +} + +MDockItem *mdock_item_new(MDockItemType type) +{ + return g_object_new(MDOCK_TYPE_ITEM, "type", type, NULL); +} + +MDockItemType mdock_item_get_item_type(MDockItem *self) +{ + return self->priv->type; +} + +void mdock_item_set_item_type(MDockItem *self, MDockItemType type) +{ + g_object_set(self, "type", type, NULL); +} + +GDesktopAppInfo *mdock_item_get_desktop_app_info(MDockItem *self) +{ + return g_object_ref(self->priv->appinfo); +} + +void mdock_item_set_desktop_app_info(MDockItem *self, GDesktopAppInfo *app_info) +{ + g_object_set(self, "desktop-app-info", app_info, NULL); +} + +void mdock_item_add_window(MDockItem *self, WnckWindow *window) +{ + self->priv->windows = g_list_append(self->priv->windows, window); + if (!self->priv->icon) { + mdock_item_update_icon(self); + } + if (!gtk_widget_get_has_tooltip(GTK_WIDGET(self))) { + mdock_item_update_tooltip(self); + } + mdock_item_update_menu(self); +} + +void mdock_item_remove_window(MDockItem *self, WnckWindow *window) +{ + self->priv->windows = g_list_remove(self->priv->windows, window); + mdock_item_update_menu(self); +} + +void mdock_item_set_last_active_window(MDockItem *self, WnckWindow *window) +{ +#if 0 /* Reorder the list of windows; put most recently used windows first. */ + GList *l = g_list_find(self->priv->windows, window); + g_return_if_fail(l != NULL); + + if (self->priv->windows == l) { + return; + } + + self->priv->windows = g_list_remove_link(self->priv->windows, l); + self->priv->windows = g_list_concat(l, self->priv->windows); +#endif +} + +gint mdock_item_get_num_windows(MDockItem *self) +{ + return g_list_length(self->priv->windows); } -MDockItem * -mdock_item_new () +void mdock_item_close_all_windows(MDockItem *self) { - return g_object_new (MDOCK_TYPE_ITEM, NULL); + for (GList *l = self->priv->windows; l; l = g_list_next(l)) { + WnckWindow *window = WNCK_WINDOW(l->data); + wnck_window_close(window, GDK_CURRENT_TIME); + } } |