/* * Copyright (C) 2008 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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 . * * Authored by Neil Jagdish Patel * */ #include "task-title.h" #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include #include #if GTK_CHECK_VERSION (3, 0, 0) #include #define MATE_DESKTOP_USE_UNSTABLE_API #include #endif #include "task-list.h" G_DEFINE_TYPE (TaskTitle, task_title, GTK_TYPE_EVENT_BOX); #define TASK_TITLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ TASK_TYPE_TITLE, \ TaskTitlePrivate)) #define LOGOUT "mate-session-save --kill --gui" #define SHOW_HOME_TITLE_KEY "show-home-title" struct _TaskTitlePrivate { WnckScreen *screen; WnckWindow *window; GtkWidget *align; GtkWidget *box; GtkWidget *label; GtkWidget *button; GtkWidget *button_image; GdkPixbuf *quit_icon; gboolean show_home_title; gboolean mouse_in_close_button; }; static void disconnect_window (TaskTitle *title); static gboolean on_close_clicked (GtkButton *button, GdkEventButton *event, TaskTitle *title) { TaskTitlePrivate *priv; WnckWindow *window; g_return_val_if_fail (TASK_IS_TITLE (title), FALSE); priv = title->priv; if (event->button != 1 || !priv->mouse_in_close_button) return FALSE; window = wnck_screen_get_active_window (priv->screen); if (!WNCK_IS_WINDOW (window) || wnck_window_get_window_type (window) == WNCK_WINDOW_DESKTOP) { #if GTK_CHECK_VERSION (3, 0, 0) mate_gdk_spawn_command_line_on_screen (gdk_screen_get_default (), #else gdk_spawn_command_line_on_screen (gdk_screen_get_default (), #endif LOGOUT, NULL); } else { if (priv->window == window) disconnect_window (title); wnck_window_close (window, GDK_CURRENT_TIME); } gtk_widget_queue_draw (GTK_WIDGET (title)); return TRUE; } static gboolean on_enter_notify (GtkWidget *widget, GdkEventCrossing *event, TaskTitle *title) { g_return_val_if_fail (TASK_IS_TITLE (title), FALSE); title->priv->mouse_in_close_button = TRUE; gtk_widget_queue_draw (widget); return FALSE; } static gboolean on_leave_notify (GtkWidget *widget, GdkEventCrossing *event, TaskTitle *title) { g_return_val_if_fail (TASK_IS_TITLE (title), FALSE); title->priv->mouse_in_close_button = FALSE; gtk_widget_queue_draw (widget); return FALSE; } static gboolean #if GTK_CHECK_VERSION (3, 0, 0) on_button_draw (GtkWidget *widget, cairo_t *cr, #else on_button_expose (GtkWidget *widget, GdkEventExpose *event, #endif TaskTitle *title) { g_return_val_if_fail (TASK_IS_TITLE (title), FALSE); TaskTitlePrivate *priv; priv = title->priv; if (priv->mouse_in_close_button) { GtkStyle *style = gtk_widget_get_style (widget); #if GTK_CHECK_VERSION (3, 0, 0) GdkRectangle area; gdouble x1, y1, x2, y2; cairo_clip_extents (cr, &x1, &y1, &x2, &y2); area.x = floor (x1); area.y = floor (y1); area.width = ceil (x2) - area.x; area.height = ceil (y2) - area.y; #endif gtk_paint_box (style, #if GTK_CHECK_VERSION (3, 0, 0) cr, #else event->window, #endif GTK_STATE_PRELIGHT, GTK_SHADOW_NONE, #if !GTK_CHECK_VERSION (3, 0, 0) NULL, #endif NULL, NULL, #if GTK_CHECK_VERSION (3, 0, 0) area.x, area.y + 2, area.width, area.height - 4); #else event->area.x, event->area.y + 2, event->area.width, event->area.height - 4); #endif } return FALSE; } static void on_name_changed (WnckWindow *window, TaskTitle *title) { TaskTitlePrivate *priv; g_return_if_fail (TASK_IS_TITLE (title)); g_return_if_fail (WNCK_IS_WINDOW (window)); priv = title->priv; if (priv->window != window) return; gtk_label_set_text (GTK_LABEL (title->priv->label), wnck_window_get_name (window)); gtk_widget_set_tooltip_text (GTK_WIDGET (title), wnck_window_get_name (window)); gtk_widget_queue_draw (GTK_WIDGET (title)); } static void on_icon_changed (WnckWindow *window, TaskTitle *title) { TaskTitlePrivate *priv; g_return_if_fail (TASK_IS_TITLE (title)); g_return_if_fail (WNCK_IS_WINDOW (window)); priv = title->priv; if (priv->window != window) return; gtk_widget_queue_draw (GTK_WIDGET (title)); } static void on_state_changed (WnckWindow *window, WnckWindowState changed_mask, WnckWindowState new_state, TaskTitle *title) { TaskTitlePrivate *priv; g_return_if_fail (TASK_IS_TITLE (title)); g_return_if_fail (WNCK_IS_WINDOW (window)); priv = title->priv; if (priv->window != window) return; if (wnck_window_is_maximized (window)) { gtk_widget_set_state (GTK_WIDGET (title), GTK_STATE_ACTIVE); gtk_widget_show (priv->box); } else { gtk_widget_set_state (GTK_WIDGET (title), GTK_STATE_NORMAL); gtk_widget_hide (priv->box); } } static void disconnect_window (TaskTitle *title) { TaskTitlePrivate *priv = title->priv; if (!priv->window) return; g_signal_handlers_disconnect_by_func (priv->window, on_name_changed, title); g_signal_handlers_disconnect_by_func (priv->window, on_icon_changed, title); g_signal_handlers_disconnect_by_func (priv->window, on_state_changed, title); priv->window = NULL; } static void on_active_window_changed (WnckScreen *screen, WnckWindow *old_window, TaskTitle *title) { TaskTitlePrivate *priv; WnckWindow *act_window; WnckWindowType type = WNCK_WINDOW_NORMAL; g_return_if_fail (TASK_IS_TITLE (title)); priv = title->priv; act_window = wnck_screen_get_active_window (screen); if (act_window) type = wnck_window_get_window_type (act_window); if (WNCK_IS_WINDOW (act_window) && wnck_window_is_skip_tasklist (act_window) && type != WNCK_WINDOW_DESKTOP) return; if (type == WNCK_WINDOW_DOCK || type == WNCK_WINDOW_SPLASHSCREEN || type == WNCK_WINDOW_MENU) return; disconnect_window (title); if (!WNCK_IS_WINDOW (act_window) || wnck_window_get_window_type (act_window) == WNCK_WINDOW_DESKTOP) { if (priv->show_home_title) { gtk_label_set_text (GTK_LABEL (priv->label), _("Home")); gtk_image_set_from_pixbuf (GTK_IMAGE (priv->button_image), priv->quit_icon); gtk_widget_set_tooltip_text (priv->button, _("Log off, switch user, lock screen or power " "down the computer")); gtk_widget_set_tooltip_text (GTK_WIDGET (title), _("Home")); gtk_widget_show (priv->box); } else { gtk_widget_set_state (GTK_WIDGET (title), GTK_STATE_NORMAL); gtk_widget_set_tooltip_text (priv->button, NULL); gtk_widget_set_tooltip_text (GTK_WIDGET (title), NULL); gtk_widget_hide (priv->box); } } else { gtk_label_set_text (GTK_LABEL (priv->label), wnck_window_get_name (act_window)); gtk_image_set_from_stock (GTK_IMAGE (priv->button_image), GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); gtk_widget_set_tooltip_text (GTK_WIDGET (title), wnck_window_get_name (act_window)); gtk_widget_set_tooltip_text (priv->button, _("Close window")); g_signal_connect (act_window, "name-changed", G_CALLBACK (on_name_changed), title); g_signal_connect (act_window, "icon-changed", G_CALLBACK (on_icon_changed), title); g_signal_connect_after (act_window, "state-changed", G_CALLBACK (on_state_changed), title); gtk_widget_show (priv->box); priv->window = act_window; } if (WNCK_IS_WINDOW (act_window) && !wnck_window_is_maximized (act_window) && (priv->show_home_title ? type != WNCK_WINDOW_DESKTOP : 1)) { gtk_widget_set_state (GTK_WIDGET (title), GTK_STATE_NORMAL); gtk_widget_hide (priv->box); } else if (!WNCK_IS_WINDOW (act_window)) { if (task_list_get_desktop_visible (TASK_LIST (task_list_get_default ())) && priv->show_home_title) { gtk_label_set_text (GTK_LABEL (priv->label), _("Home")); gtk_image_set_from_pixbuf (GTK_IMAGE (priv->button_image), priv->quit_icon); gtk_widget_set_tooltip_text (priv->button, _("Log off, switch user, lock screen or power " "down the computer")); gtk_widget_set_tooltip_text (GTK_WIDGET (title), _("Home")); gtk_widget_show (priv->box); } else { gtk_widget_set_state (GTK_WIDGET (title), GTK_STATE_NORMAL); gtk_widget_set_tooltip_text (priv->button, NULL); gtk_widget_set_tooltip_text (GTK_WIDGET (title), NULL); gtk_widget_hide (priv->box); } } else gtk_widget_set_state (GTK_WIDGET (title), GTK_STATE_ACTIVE); gtk_widget_queue_draw (GTK_WIDGET (title)); } static gboolean on_button_release (GtkWidget *title, GdkEventButton *event) { TaskTitlePrivate *priv; WnckWindow *window; GtkWidget *menu; g_return_val_if_fail (TASK_IS_TITLE (title), FALSE); priv = TASK_TITLE_GET_PRIVATE (title); window = wnck_screen_get_active_window (priv->screen); g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE); if (event->button == 3) { if (wnck_window_get_window_type (window) != WNCK_WINDOW_DESKTOP) { menu = wnck_action_menu_new (window); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time); return TRUE; } } else if (event->button == 1) { if (event->type == GDK_2BUTTON_PRESS && wnck_window_is_maximized (window)) { wnck_window_unmaximize (window); } } return FALSE; } static gboolean #if GTK_CHECK_VERSION (3, 0, 0) on_draw (GtkWidget *eb, cairo_t *cr) #else on_expose (GtkWidget *eb, GdkEventExpose *event) #endif { GtkAllocation *allocation; GtkStyle *style; GtkStateType state; gtk_widget_get_allocation (eb, allocation); style = gtk_widget_get_style (eb); state = gtk_widget_get_state (eb); if (state == GTK_STATE_ACTIVE && allocation) gtk_paint_box (style, #if GTK_CHECK_VERSION (3, 0, 0) cr, #else eb->window, #endif state, GTK_SHADOW_NONE, #if !GTK_CHECK_VERSION (3, 0, 0) NULL, #endif eb, "button", allocation->x, allocation->y, allocation->width, allocation->height); #if GTK_CHECK_VERSION (3, 0, 0) gtk_container_propagate_draw (GTK_CONTAINER (eb), gtk_bin_get_child (GTK_BIN (eb)), cr); #else gtk_container_propagate_expose (GTK_CONTAINER (eb), gtk_bin_get_child (GTK_BIN (eb)), event); #endif return TRUE; } /* GObject stuff */ static void task_title_finalize (GObject *object) { TaskTitlePrivate *priv; priv = TASK_TITLE_GET_PRIVATE (object); disconnect_window (TASK_TITLE (object)); g_object_unref (G_OBJECT (priv->quit_icon)); G_OBJECT_CLASS (task_title_parent_class)->finalize (object); } static void task_title_class_init (TaskTitleClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GtkWidgetClass *wid_class = GTK_WIDGET_CLASS (klass); obj_class->finalize = task_title_finalize; #if GTK_CHECK_VERSION (3, 0, 0) wid_class->draw = on_draw; #else wid_class->expose_event = on_expose; #endif g_type_class_add_private (obj_class, sizeof (TaskTitlePrivate)); } static void task_title_init (TaskTitle *title) { TaskTitlePrivate *priv; GdkScreen *gdkscreen; GtkIconTheme *theme; GtkSettings *settings; GdkPixbuf *pixbuf; AtkObject *atk; int width, height; priv = title->priv = TASK_TITLE_GET_PRIVATE (title); priv->screen = wnck_screen_get_default (); priv->window = NULL; /* FIXME we can add an option for this in future */ /* now it's disabled with gsettings migration */ priv->show_home_title = FALSE; gtk_widget_add_events (GTK_WIDGET (title), GDK_ALL_EVENTS_MASK); priv->align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0); gtk_alignment_set_padding (GTK_ALIGNMENT (priv->align), 0, 0, 6, 6); gtk_container_add (GTK_CONTAINER (title), priv->align); priv->box = gtk_hbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER (priv->align), priv->box); gtk_widget_set_no_show_all (priv->box, TRUE); gtk_widget_show (priv->box); priv->label = gtk_label_new (_("Home")); gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END); gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5); PangoAttrList *attr_list = pango_attr_list_new (); PangoAttribute *attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); pango_attr_list_insert (attr_list, attr); gtk_label_set_attributes (GTK_LABEL (priv->label), attr_list); gtk_box_pack_start (GTK_BOX (priv->box), priv->label, TRUE, TRUE, 0); gtk_widget_show (priv->label); priv->button = g_object_new (GTK_TYPE_EVENT_BOX, "visible-window", FALSE, "above-child", TRUE, NULL); gtk_box_pack_start (GTK_BOX (priv->box), priv->button, FALSE, FALSE, 0); gtk_widget_show (priv->button); atk = gtk_widget_get_accessible (priv->button); atk_object_set_name (atk, _("Close")); atk_object_set_description (atk, _("Close current window.")); atk_object_set_role (atk, ATK_ROLE_PUSH_BUTTON); g_signal_connect (priv->button, "button-release-event", G_CALLBACK (on_close_clicked), title); g_signal_connect (priv->button, "enter-notify-event", G_CALLBACK (on_enter_notify), title); g_signal_connect (priv->button, "leave-notify-event", G_CALLBACK (on_leave_notify), title); #if GTK_CHECK_VERSION (3, 0, 0) g_signal_connect (priv->button, "draw", G_CALLBACK (on_button_draw), title); #else g_signal_connect (priv->button, "expose-event", G_CALLBACK (on_button_expose), title); #endif /* Load the quit icon. We have to do this in such a god-forsaken way because of http://bugzilla.gnome.org/show_bug.cgi?id=581359 and the fact that we support as far back as GTK+ 2.12 (which never passes FORCE_SIZE in GtkImage). The only way to guarantee icon size is to load and scale it ourselves. We don't do this for all the other icons in the source just because we know that this icon doesn't come in the size we want. */ gdkscreen = gtk_widget_get_screen (GTK_WIDGET (title)); theme = gtk_icon_theme_get_for_screen (gdkscreen); settings = gtk_settings_get_for_screen (gdkscreen); if (!gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height)) width = height = 16; width = MIN(width, height); pixbuf = gtk_icon_theme_load_icon (theme, "mate-logout", width, 0, NULL); priv->quit_icon = gdk_pixbuf_scale_simple (pixbuf, width, width, GDK_INTERP_BILINEAR); g_object_unref (G_OBJECT (pixbuf)); priv->button_image = gtk_image_new_from_pixbuf (priv->quit_icon); gtk_container_add (GTK_CONTAINER (priv->button), priv->button_image); gtk_widget_show (priv->button_image); gtk_widget_set_tooltip_text (priv->button, _("Log off, switch user, lock screen or power " "down the computer")); gtk_widget_set_tooltip_text (GTK_WIDGET (title), _("Home")); if (priv->show_home_title) gtk_widget_set_state (GTK_WIDGET (title), GTK_STATE_ACTIVE); else gtk_widget_hide (priv->box); gtk_widget_add_events (GTK_WIDGET (title), GDK_ALL_EVENTS_MASK); g_signal_connect (priv->screen, "active-window-changed", G_CALLBACK (on_active_window_changed), title); g_signal_connect (title, "button-press-event", G_CALLBACK (on_button_release), NULL); } GtkWidget * task_title_new (void) { GtkWidget *title = NULL; title = g_object_new (TASK_TYPE_TITLE, "border-width", 0, "name", "tasklist-button", "visible-window", FALSE, NULL); return title; }