/* * Copyright 2015 Javier S. Pedro * * This file is part of MDock. * * MDock 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, either version 3 of the License, or * (at your option) any later version. * * MDock 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 Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MDock. If not, see . */ #include #define WNCK_I_KNOW_THIS_IS_UNSTABLE 1 #include #include "mdock-widget.h" #include "mdock-item.h" #include "matcher.h" struct _MDockWidgetPrivate { gchar *settings_path; GSettings *settings; WnckScreen *wnck_screen; GSequence *items; GHashTable *appid_to_item; GHashTable *desktopid_to_item; GHashTable *window_to_item; }; G_DEFINE_TYPE(MDockWidget, mdock_widget, GTK_TYPE_BOX) #define MDOCK_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), MDOCK_TYPE_WIDGET, MDockWidgetPrivate)) enum { PROP_0, PROP_SETTINGS_PATH, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = { NULL }; static void handle_active_window_changed(MDockWidget *self, WnckWindow *previous_window, WnckScreen *screen) { WnckWindow *window = wnck_screen_get_active_window(screen); GSequenceIter *iter = g_hash_table_lookup(self->priv->window_to_item, window); if (iter) { MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_set_last_active_window(item, window); } } static gboolean filter_deleted_group(gpointer key, gpointer value, gpointer user_data) { return value == user_data; } static void handle_window_closed(MDockWidget *self, WnckWindow *window, WnckScreen *screen) { GSequenceIter *iter = g_hash_table_lookup(self->priv->window_to_item, window); if (!iter) { // We never managed this window return; } MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_remove_window(item, window); g_hash_table_remove(self->priv->window_to_item, window); if (mdock_item_get_num_windows(item) == 0) { // Removing window! g_hash_table_foreach_remove(self->priv->appid_to_item, filter_deleted_group, iter); g_hash_table_foreach_remove(self->priv->desktopid_to_item, filter_deleted_group, iter); g_sequence_remove(iter); gtk_widget_destroy(GTK_WIDGET(item)); } } static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScreen *screen) { if (wnck_window_is_skip_tasklist(window)) { return; } AppId *appid = app_id_from_window(window); GSequenceIter *iter = g_hash_table_lookup(self->priv->appid_to_item, appid); if (iter) { MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_add_window(item, window); app_id_destroy(appid); g_hash_table_insert(self->priv->window_to_item, window, iter); return; } const gchar *desktopid = match_appid_to_desktopid(appid); if (desktopid) { iter = g_hash_table_lookup(self->priv->desktopid_to_item, desktopid); if (iter) { MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_add_window(item, window); g_hash_table_insert(self->priv->appid_to_item, appid, iter); // takes ownership of appid g_hash_table_insert(self->priv->window_to_item, window, iter); return; } } MDockItem *item = mdock_item_new(MDOCK_ITEM_TYPE_APPLICATION); iter = g_sequence_append(self->priv->items, item); if (desktopid) { GDesktopAppInfo *app_info = g_desktop_app_info_new(desktopid); mdock_item_set_desktop_app_info(item, app_info); g_object_unref(app_info); } g_settings_bind(self->priv->settings, "icon-size", item, "icon-size", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); mdock_item_add_window(item, window); g_hash_table_insert(self->priv->appid_to_item, appid, iter); // takes ownership of appid if (desktopid) { g_hash_table_insert(self->priv->desktopid_to_item, g_strdup(desktopid), iter); } g_hash_table_insert(self->priv->window_to_item, window, iter); gtk_widget_show(GTK_WIDGET(item)); gtk_box_pack_start(GTK_BOX(self), GTK_WIDGET(item), TRUE, FALSE, 0); } static void mdock_widget_constructed(GObject *obj) { MDockWidget *self = MDOCK_WIDGET(obj); g_debug("Constructing with path: %s", self->priv->settings_path); self->priv->settings = g_settings_new_with_path("com.javispedro.mdock.widget", self->priv->settings_path); self->priv->wnck_screen = wnck_screen_get_default(); g_signal_connect_object(self->priv->wnck_screen, "active-window-changed", G_CALLBACK(handle_active_window_changed), self, G_CONNECT_SWAPPED); g_signal_connect_object(self->priv->wnck_screen, "window-closed", G_CALLBACK(handle_window_closed), self, G_CONNECT_SWAPPED); g_signal_connect_object(self->priv->wnck_screen, "window-opened", G_CALLBACK(handle_window_opened), self, G_CONNECT_SWAPPED); self->priv->items = g_sequence_new(NULL); self->priv->appid_to_item = g_hash_table_new_full(app_id_hash, app_id_equal, (GDestroyNotify)app_id_destroy, NULL); self->priv->desktopid_to_item = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); self->priv->window_to_item = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); GList *windows = wnck_screen_get_windows_stacked(self->priv->wnck_screen); for (GList *l = windows; l; l = g_list_next(l)) { WnckWindow *window = l->data; handle_window_opened(self, window, self->priv->wnck_screen); } } static void mdock_widget_dispose(GObject *obj) { MDockWidget *self = MDOCK_WIDGET(obj); if (self->priv->settings) { g_clear_object(&self->priv->settings); } if (self->priv->wnck_screen) { g_signal_handlers_disconnect_by_data(self->priv->wnck_screen, self); self->priv->wnck_screen = NULL; } G_OBJECT_CLASS(mdock_widget_parent_class)->dispose(obj); } static void mdock_widget_finalize(GObject *obj) { MDockWidget *self = MDOCK_WIDGET(obj); g_free(self->priv->settings_path); g_sequence_free(self->priv->items); g_hash_table_destroy(self->priv->appid_to_item); g_hash_table_destroy(self->priv->desktopid_to_item); g_hash_table_destroy(self->priv->window_to_item); G_OBJECT_CLASS(mdock_widget_parent_class)->finalize(obj); } static void mdock_widget_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MDockWidget *self = MDOCK_WIDGET(object); switch (property_id) { case PROP_SETTINGS_PATH: self->priv->settings_path = g_value_dup_string(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void mdock_widget_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MDockWidget *self = MDOCK_WIDGET(object); switch (property_id) { case PROP_SETTINGS_PATH: g_value_set_string(value, self->priv->settings_path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void mdock_widget_init(MDockWidget *self) { self->priv = MDOCK_WIDGET_GET_PRIVATE(self); } static void mdock_widget_class_init(MDockWidgetClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->constructed = mdock_widget_constructed; obj_class->dispose = mdock_widget_dispose; obj_class->finalize = mdock_widget_finalize; obj_class->set_property = mdock_widget_set_property; obj_class->get_property = mdock_widget_get_property; obj_properties[PROP_SETTINGS_PATH] = g_param_spec_string("settings-path", "Path to GSettings for this widget", "Set the path to GSettings for this widget", "/com/javispedro/mdock/standalone/", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, N_PROPERTIES, obj_properties); g_type_class_add_private(klass, sizeof(MDockWidgetPrivate)); } GtkWidget *mdock_widget_new(void) { return GTK_WIDGET(g_object_new(MDOCK_TYPE_WIDGET, NULL)); }