diff options
Diffstat (limited to 'mate-window-picker-applet/task-item.c')
-rw-r--r-- | mate-window-picker-applet/task-item.c | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/mate-window-picker-applet/task-item.c b/mate-window-picker-applet/task-item.c new file mode 100644 index 0000000..3e0dd97 --- /dev/null +++ b/mate-window-picker-applet/task-item.c @@ -0,0 +1,779 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Authored by Jason Smith <[email protected]> + * + */ + +#include "task-item.h" +#include "task-list.h" + +#include <math.h> +#include <glib/gi18n.h> +#include <cairo/cairo.h> + +G_DEFINE_TYPE (TaskItem, task_item, GTK_TYPE_EVENT_BOX); + +#define TASK_ITEM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ + TASK_TYPE_ITEM, \ + TaskItemPrivate)) + +#define DEFAULT_TASK_ITEM_HEIGHT 24; +#define DEFAULT_TASK_ITEM_WIDTH 28 + +struct _TaskItemPrivate +{ + MatewnckWindow *window; + MatewnckScreen *screen; + GdkPixbuf *pixbuf; + GdkRectangle area; + + GTimeVal urgent_time; + guint timer; + gboolean mouse_over; +}; + +enum { + TASK_ITEM_CLOSED_SIGNAL, + LAST_SIGNAL +}; + +/* D&D stuff */ +static const GtkTargetEntry drop_types[] = +{ + { "STRING", 0, 0 }, + { "text/plain", 0, 0}, + { "text/uri-list", 0, 0} +}; +static const gint n_drop_types = G_N_ELEMENTS(drop_types); + +static guint task_item_signals[LAST_SIGNAL] = { 0 }; + +static void +update_hints (TaskItem *item) +{ + GtkWidget *parent; + GtkWidget *widget; + MatewnckWindow *window; + gint x, y, x1, y1; + + widget = GTK_WIDGET (item); + window = item->priv->window; + /* Skip problems */ + if (!MATEWNCK_IS_WINDOW (window)) return; + if (!GTK_IS_WIDGET (widget)) return; + + /* Skip invisible windows */ + if (!GTK_WIDGET_VISIBLE (widget)) return; + + x = y = 0; + + /* Recursively compute the button's coordinates */ + for (parent = widget; parent; parent = parent->parent) + { + if (parent->parent) + { + x += parent->allocation.x; + y += parent->allocation.y; + } + else + { + x1 = y1 = 0; + if (GDK_IS_WINDOW (parent->window)) + gdk_window_get_origin (parent->window, &x1, &y1); + x += x1; y += y1; + break; + } + } + + /* Set the minimize hint for the window */ + matewnck_window_set_icon_geometry (window, x, y, + widget->allocation.width, + widget->allocation.height); +} + +static gboolean +on_task_item_button_released (GtkWidget *widget, + GdkEventButton *event, + TaskItem *item) +{ + MatewnckWindow *window; + MatewnckScreen *screen; + MatewnckWorkspace *workspace; + TaskItemPrivate *priv; + + g_return_val_if_fail (TASK_IS_ITEM (item), TRUE); + + priv = item->priv; + window = priv->window; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), TRUE); + + screen = priv->screen; + workspace = matewnck_window_get_workspace (window); + + if (event->button == 1) + { + + if (MATEWNCK_IS_WORKSPACE (workspace) && workspace != matewnck_screen_get_active_workspace (screen)) + { + matewnck_workspace_activate (workspace, GDK_CURRENT_TIME); + } + if (matewnck_window_is_active (window)) + { + matewnck_window_minimize (window); + } + else + { + matewnck_window_activate (window, GDK_CURRENT_TIME); + } + } + return TRUE; +} + +static void +task_item_set_visibility (TaskItem *item) +{ + MatewnckScreen *screen; + MatewnckWindow *window; + MatewnckWorkspace *workspace; + + g_return_if_fail (TASK_IS_ITEM (item)); + + TaskItemPrivate *priv = item->priv; + + if (!MATEWNCK_IS_WINDOW (priv->window)) + { + gtk_widget_hide (GTK_WIDGET (item)); + return; + } + + window = priv->window; + + screen = priv->screen; + workspace = matewnck_screen_get_active_workspace (screen); + + gboolean show_all = task_list_get_show_all_windows (TASK_LIST (task_list_get_default ())); + gboolean show_window = FALSE; + + if (!matewnck_window_is_skip_tasklist (window)) + { + if (matewnck_workspace_is_virtual (workspace)) + { + show_window = matewnck_window_is_in_viewport (window, workspace); + } + else + { + show_window = matewnck_window_is_on_workspace (window, workspace); + } + show_window = show_window || show_all; + } + + if (show_window) + { + gtk_widget_show (GTK_WIDGET (item)); + } + else + { + gtk_widget_hide (GTK_WIDGET (item)); + } +} + +static void +task_item_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + /* Candidate for terrible hack of the year award */ + requisition->width = DEFAULT_TASK_ITEM_WIDTH; + requisition->height = DEFAULT_TASK_ITEM_HEIGHT; +} + +static GdkPixbuf * +task_item_sized_pixbuf_for_window (TaskItem *item, + MatewnckWindow *window, + gint size) +{ + GdkPixbuf *pbuf = NULL; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), NULL); + + if (matewnck_window_has_icon_name (window)) + { + const gchar *icon_name = matewnck_window_get_icon_name (window); + GtkIconTheme *icon_theme = gtk_icon_theme_get_default (); + + if (gtk_icon_theme_has_icon (icon_theme, icon_name)) + { + GdkPixbuf *internal = gtk_icon_theme_load_icon (icon_theme, + icon_name, + size, + GTK_ICON_LOOKUP_FORCE_SIZE, + NULL); + pbuf = gdk_pixbuf_copy (internal); + g_object_unref (internal); + } + } + + if (!pbuf) + { + pbuf = gdk_pixbuf_copy (matewnck_window_get_icon (item->priv->window)); + } + + gint width = gdk_pixbuf_get_width (pbuf); + gint height = gdk_pixbuf_get_height (pbuf); + + if (MAX (width, height) != size) + { + gdouble scale = (gdouble) size / (gdouble) MAX (width, height); + + GdkPixbuf *tmp = pbuf; + pbuf = gdk_pixbuf_scale_simple (tmp, (gint) (width * scale), (gint) (height * scale), GDK_INTERP_HYPER); + + g_object_unref (tmp); + } + + return pbuf; +} +static gboolean +task_item_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + cairo_t *cr; + TaskItem *item; + GdkRectangle area; + TaskItemPrivate *priv; + GdkPixbuf *desat; + GdkPixbuf *pbuf; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (TASK_IS_ITEM (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + item = TASK_ITEM (widget); + priv = item->priv; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (priv->window), FALSE); + + area = priv->area; + cr = gdk_cairo_create (event->window); + + pbuf = priv->pixbuf; + desat = NULL; + + gint size = MIN (area.height, area.width); + gboolean active = matewnck_window_is_active (priv->window); + gboolean attention = matewnck_window_or_transient_needs_attention (priv->window); + + if (GDK_IS_PIXBUF (pbuf) && + gdk_pixbuf_get_width (pbuf) != size && + gdk_pixbuf_get_height (pbuf) != size) + { + g_object_unref (pbuf); + pbuf = NULL; + } + + if (active) + { + cairo_rectangle (cr, area.x + .5, area.y - 4, area.width - 1, area.height + 8); + cairo_set_source_rgba (cr, .8, .8, .8, .2); + cairo_fill_preserve (cr); + + cairo_set_line_width (cr, 1); + cairo_set_source_rgba (cr, .8, .8, .8, .4); + cairo_stroke (cr); + } + + if (!pbuf) + { + pbuf = priv->pixbuf = task_item_sized_pixbuf_for_window (item, priv->window, size); + } + + if (active || priv->mouse_over || attention) + { + gdk_cairo_set_source_pixbuf (cr, + pbuf, + (area.x + (area.width - gdk_pixbuf_get_width (pbuf)) / 2), + (area.y + (area.height - gdk_pixbuf_get_height (pbuf)) / 2)); + } + else + { + desat = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + gdk_pixbuf_get_bits_per_sample (pbuf), + gdk_pixbuf_get_width (pbuf), + gdk_pixbuf_get_height (pbuf)); + + if (desat) + { + gdk_pixbuf_saturate_and_pixelate (pbuf, + desat, + 0, + FALSE); + } + else /* just paint the colored version as a fallback */ + { + desat = pbuf; + } + gdk_cairo_set_source_pixbuf (cr, + desat, + (area.x + (area.width - gdk_pixbuf_get_width (desat)) / 2), + (area.y + (area.height - gdk_pixbuf_get_height (desat)) / 2)); + } + if (!priv->mouse_over && attention) /* urgent */ + { + GTimeVal current_time; + g_get_current_time (¤t_time); + + gdouble ms = (current_time.tv_sec - priv->urgent_time.tv_sec) * 1000 + + (current_time.tv_usec - priv->urgent_time.tv_usec) / 1000; + + gdouble alpha = .66 + (cos (3.15 * ms / 600) / 3); + cairo_paint_with_alpha (cr, alpha); + } + else if (priv->mouse_over || active) /* focused */ + { + cairo_paint (cr); + } + else /* not focused */ + { + cairo_paint_with_alpha (cr, .65); + } + + if (GDK_IS_PIXBUF (desat)) + g_object_unref (desat); + + cairo_destroy (cr); + + return FALSE; +} + +static void +on_size_allocate (GtkWidget *widget, + GtkAllocation *allocation, + TaskItem *item) +{ + TaskItemPrivate *priv; + + if (allocation->width != allocation->height + 6) + gtk_widget_set_size_request (widget, allocation->height + 6, -1); + + g_return_if_fail (TASK_IS_ITEM (item)); + + priv = item->priv; + priv->area.x = allocation->x; + priv->area.y = allocation->y; + priv->area.width = allocation->width; + priv->area.height = allocation->height; + + update_hints (item); +} + +static gboolean +on_button_pressed (GtkWidget *button, + GdkEventButton *event, + TaskItem *item) +{ + MatewnckWindow *window; + g_return_val_if_fail (TASK_IS_ITEM (item), FALSE); + window = item->priv->window; + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + if (event->button == 3) + { + GtkWidget *menu = matewnck_action_menu_new (window); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, + event->button, event->time); + return TRUE; + } + + return FALSE; +} + +static gboolean +on_query_tooltip (GtkWidget *widget, + gint x, gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + TaskItem *item) +{ + MatewnckWindow *window = item->priv->window; + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + gtk_tooltip_set_text (tooltip, matewnck_window_get_name(window)); + gtk_tooltip_set_icon (tooltip, matewnck_window_get_icon (window)); + + return TRUE; +} + +static gboolean +on_enter_notify (GtkWidget *widget, + GdkEventCrossing *event, + TaskItem *item) +{ + g_return_val_if_fail (TASK_IS_ITEM (item), FALSE); + + item->priv->mouse_over = TRUE; + gtk_widget_queue_draw (widget); + + return FALSE; +} + +static gboolean +on_leave_notify (GtkWidget *widget, + GdkEventCrossing *event, + TaskItem *item) +{ + g_return_val_if_fail (TASK_IS_ITEM (item), FALSE); + + item->priv->mouse_over = FALSE; + gtk_widget_queue_draw (widget); + + return FALSE; +} + +static gboolean +on_blink (TaskItem *item) +{ + g_return_val_if_fail (TASK_IS_ITEM (item), FALSE); + + gtk_widget_queue_draw (GTK_WIDGET (item)); + + if (matewnck_window_or_transient_needs_attention (item->priv->window)) + { + return TRUE; + } + else + { + item->priv->timer = 0; + return FALSE; + } +} + +static void +on_window_state_changed (MatewnckWindow *window, + MatewnckWindowState changed_mask, + MatewnckWindowState new_state, + TaskItem *item) +{ + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + g_return_if_fail (TASK_IS_ITEM (item)); + + TaskItemPrivate *priv = item->priv; + + if (new_state & MATEWNCK_WINDOW_STATE_URGENT && !priv->timer) + { + priv->timer = g_timeout_add (30, (GSourceFunc)on_blink, item); + g_get_current_time (&priv->urgent_time); + } + + task_item_set_visibility (item); +} + +static void +on_window_workspace_changed (MatewnckWindow *window, TaskItem *item) +{ + g_return_if_fail (TASK_IS_ITEM (item)); + + task_item_set_visibility (item); +} + +static void on_window_icon_changed (MatewnckWindow *window, TaskItem *item) +{ + TaskItemPrivate *priv; + + g_return_if_fail (TASK_IS_ITEM (item)); + + priv = item->priv; + + if (GDK_IS_PIXBUF (priv->pixbuf)) + { + g_object_unref (priv->pixbuf); + priv->pixbuf = NULL; + } + + gtk_widget_queue_draw (GTK_WIDGET (item)); +} + +static void +on_screen_active_window_changed (MatewnckScreen *screen, + MatewnckWindow *old_window, + TaskItem *item) +{ + MatewnckWindow *window; + TaskItemPrivate *priv; + + g_return_if_fail (TASK_IS_ITEM (item)); + + priv = item->priv; + window = priv->window; + + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + + if ((MATEWNCK_IS_WINDOW (old_window) && window == old_window) || + window == matewnck_screen_get_active_window (screen)) + { + /* queue a draw to reflect that we are [no longer] the active window */ + gtk_widget_queue_draw (GTK_WIDGET (item)); + } +} + +static void +on_screen_active_workspace_changed (MatewnckScreen *screen, + MatewnckWorkspace *old_workspace, + TaskItem *item) +{ + g_return_if_fail (TASK_IS_ITEM (item)); + + task_item_set_visibility (item); +} + +static void +on_screen_active_viewport_changed (MatewnckScreen *screen, + TaskItem *item) +{ + g_return_if_fail (TASK_IS_ITEM (item)); + + task_item_set_visibility (item); +} + +static void +on_screen_window_closed (MatewnckScreen *screen, + MatewnckWindow *window, + TaskItem *item) +{ + TaskItemPrivate *priv; + + g_return_if_fail (TASK_IS_ITEM (item)); + priv = item->priv; + g_return_if_fail (MATEWNCK_IS_WINDOW (priv->window)); + + if (priv->window == window) + { + g_signal_handlers_disconnect_by_func (screen, G_CALLBACK (on_screen_window_closed), item); + g_signal_handlers_disconnect_by_func (screen, G_CALLBACK (on_screen_active_window_changed), item); + g_signal_handlers_disconnect_by_func (screen, G_CALLBACK (on_screen_active_workspace_changed), item); + g_signal_handlers_disconnect_by_func (screen, G_CALLBACK (on_screen_window_closed), item); + g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_window_workspace_changed), item); + g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_window_state_changed), item); + + g_signal_emit (G_OBJECT (item), task_item_signals[TASK_ITEM_CLOSED_SIGNAL], 0); + } +} + +static gboolean +activate_window (GtkWidget *widget) +{ + gint active; + TaskItemPrivate *priv; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (TASK_IS_ITEM (widget), FALSE); + + priv = TASK_ITEM (widget)->priv; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (priv->window), FALSE); + + active = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "drag-true")); + + if (active) + { + MatewnckWindow *window; + + window = priv->window; + if (MATEWNCK_IS_WINDOW (window)) + matewnck_window_activate (window, time (NULL)); + } + + g_object_set_data (G_OBJECT (widget), "drag-true", GINT_TO_POINTER (0)); + + return FALSE; +} + +static void +on_drag_leave (GtkWidget *item, + GdkDragContext *context, + guint time) +{ + g_object_set_data (G_OBJECT (item), "drag-true", GINT_TO_POINTER (0)); +} + +static gboolean +on_drag_motion (GtkWidget *item, + GdkDragContext *context, + gint x, + gint y, + guint t) +{ + gint active; + + active = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "drag-true")); + + if (!active) + { + g_object_set_data (G_OBJECT (item), "drag-true", GINT_TO_POINTER (1)); + + g_timeout_add (1000, (GSourceFunc)activate_window, item); + } + + return FALSE; +} + +static void +task_item_setup_atk (TaskItem *item) +{ + TaskItemPrivate *priv; + GtkWidget *widget; + AtkObject *atk; + MatewnckWindow *window; + + g_return_if_fail (TASK_IS_ITEM (item)); + + widget = GTK_WIDGET (item); + priv = item->priv; + window = priv->window; + + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + + atk = gtk_widget_get_accessible (widget); + atk_object_set_name (atk, _("Window Task Button")); + atk_object_set_description (atk, matewnck_window_get_name (window)); + atk_object_set_role (atk, ATK_ROLE_PUSH_BUTTON); +} + +static void +task_item_finalize (GObject *object) +{ + TaskItemPrivate *priv; + priv = TASK_ITEM_GET_PRIVATE (object); + + /* remove timer */ + if (priv->timer) + { + g_source_remove (priv->timer); + } + + if (GDK_IS_PIXBUF (priv->pixbuf)) + { + g_object_unref (priv->pixbuf); + } + + G_OBJECT_CLASS (task_item_parent_class)->finalize (object); +} + +static void +task_item_class_init (TaskItemClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + obj_class->finalize = task_item_finalize; + widget_class->expose_event = task_item_expose_event; + widget_class->size_request = task_item_size_request; + + g_type_class_add_private (obj_class, sizeof (TaskItemPrivate)); + + task_item_signals [TASK_ITEM_CLOSED_SIGNAL] = + g_signal_new ("task-item-closed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (TaskItemClass, itemclosed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static void +task_item_init (TaskItem *item) +{ + TaskItemPrivate *priv; + priv = item->priv = TASK_ITEM_GET_PRIVATE (item); + + priv->timer = 0; +} + +GtkWidget * +task_item_new (MatewnckWindow *window) +{ + GtkWidget *item = NULL; + TaskItem *task; + TaskItemPrivate *priv; + MatewnckScreen *screen; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), item); + + item = g_object_new (TASK_TYPE_ITEM, + "has-tooltip", TRUE, + "visible-window", FALSE, + "above-child", TRUE, + NULL); + + gtk_widget_add_events (item, GDK_ALL_EVENTS_MASK); + gtk_container_set_border_width (GTK_CONTAINER (item), 0); + + task = TASK_ITEM (item); + priv = task->priv; + priv->window = window; + + screen = matewnck_window_get_screen (window); + priv->screen = screen; + + gtk_drag_dest_set (item, + GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, + drop_types, n_drop_types, + GDK_ACTION_COPY); + gtk_drag_dest_add_uri_targets (item); + gtk_drag_dest_add_text_targets (item); + g_signal_connect (item, "drag-motion", + G_CALLBACK (on_drag_motion), NULL); + g_signal_connect (item, "drag-leave", + G_CALLBACK (on_drag_leave), NULL); + + g_signal_connect (screen, "viewports-changed", + G_CALLBACK (on_screen_active_viewport_changed), item); + g_signal_connect (screen, "active-window-changed", + G_CALLBACK (on_screen_active_window_changed), item); + g_signal_connect (screen, "active-workspace-changed", + G_CALLBACK (on_screen_active_workspace_changed), item); + g_signal_connect (screen, "window-closed", + G_CALLBACK (on_screen_window_closed), item); + + g_signal_connect (window, "workspace-changed", + G_CALLBACK (on_window_workspace_changed), item); + g_signal_connect (window, "state-changed", + G_CALLBACK (on_window_state_changed), item); + g_signal_connect (window, "icon-changed", + G_CALLBACK (on_window_icon_changed), item); + + g_signal_connect (item, "button-release-event", + G_CALLBACK (on_task_item_button_released), item); + g_signal_connect (item, "button-press-event", + G_CALLBACK (on_button_pressed), item); + g_signal_connect (item, "size-allocate", + G_CALLBACK (on_size_allocate), item); + g_signal_connect (item, "query-tooltip", + G_CALLBACK (on_query_tooltip), item); + g_signal_connect (item, "enter-notify-event", + G_CALLBACK (on_enter_notify), item); + g_signal_connect (item, "leave-notify-event", + G_CALLBACK (on_leave_notify), item); + g_signal_connect (item, "drag-motion", + G_CALLBACK (on_drag_motion), item); + g_signal_connect (item, "drag-leave", + G_CALLBACK (on_drag_leave), item); + + task_item_set_visibility (task); + task_item_setup_atk (task); + + return item; +} |