/*
* 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
#include
#define MATE_DESKTOP_USE_UNSTABLE_API
#include
#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)
{
mate_gdk_spawn_command_line_on_screen (gdk_screen_get_default (),
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 *w, cairo_t *cr)
#else
on_expose (GtkWidget *w, GdkEventExpose *event)
#endif
{
#if GTK_CHECK_VERSION (3, 0, 0)
if (gtk_widget_get_state_flags (w) == GTK_STATE_FLAG_ACTIVE) {
GtkStyleContext *context = gtk_widget_get_style_context (w);
gtk_render_frame (context,
cr,
0, 0,
gtk_widget_get_allocated_width (w),
gtk_widget_get_allocated_height (w));
}
#else
if (w->state == GTK_STATE_ACTIVE) {
gtk_paint_box (w->style, w->window,
w->state, GTK_SHADOW_NONE,
NULL, w, "button",
w->allocation.x, w->allocation.y,
w->allocation.width, w->allocation.height);
}
#endif
#if GTK_CHECK_VERSION (3, 0, 0)
gtk_container_propagate_draw (GTK_CONTAINER (w),
gtk_bin_get_child (GTK_BIN (w)),
cr);
#else
gtk_container_propagate_expose (GTK_CONTAINER (w),
gtk_bin_get_child (GTK_BIN (w)),
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;
}