/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */ /* vim: set sw=2 et: */ /* pager object */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Kim Woelders * Copyright (C) 2003 Red Hat, Inc. * Copyright (C) 2003, 2005-2007 Vincent Untz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, see . */ #include #include #include #include "pager.h" #include "workspace.h" #include "window.h" #include "xutils.h" #include "pager-accessible-factory.h" #include "workspace-accessible-factory.h" #include "private.h" /** * SECTION:pager * @short_description: a pager widget, showing the content of workspaces. * @see_also: #WnckScreen * @stability: Unstable * * A #WnckPager shows a miniature view of the workspaces, representing managed * windows by small rectangles, and allows the user to initiate various window * manager actions by manipulating these representations. The #WnckPager offers * ways to move windows between workspaces and to change the current workspace. * * Alternatively, a #WnckPager can be configured to only show the names of the * workspace instead of their contents. * * The #WnckPager is also responsible for setting the layout of the workspaces. * Since only one application can be responsible for setting the layout on a * screen, the #WnckPager automatically tries to obtain the manager selection * for the screen and only sets the layout if it owns the manager selection. * See wnck_pager_set_orientation() and wnck_pager_set_n_rows() to change the * layout. */ #define N_SCREEN_CONNECTIONS 11 struct _WnckPagerPrivate { WnckHandle *handle; WnckScreen *screen; int n_rows; /* really columns for vertical orientation */ WnckPagerDisplayMode display_mode; WnckPagerScrollMode scroll_mode; gboolean show_all_workspaces; GtkShadowType shadow_type; gboolean wrap_on_scroll; GtkOrientation orientation; int workspace_size; guint screen_connections[N_SCREEN_CONNECTIONS]; int prelight; /* workspace mouse is hovering over */ gboolean prelight_dnd; /* is dnd happening? */ guint dragging :1; int drag_start_x; int drag_start_y; WnckWindow *drag_window; GdkPixbuf *bg_cache; int layout_manager_token; guint dnd_activate; /* GSource that triggers switching to this workspace during dnd */ guint dnd_time; /* time of last event during dnd (for delayed workspace activation) */ }; enum { PROP_0, PROP_HANDLE, LAST_PROP }; static GParamSpec *pager_properties[LAST_PROP] = { NULL }; G_DEFINE_TYPE_WITH_PRIVATE (WnckPager, wnck_pager, GTK_TYPE_WIDGET); enum { dummy, /* remove this when you add more signals */ LAST_SIGNAL }; #define POINT_IN_RECT(xcoord, ycoord, rect) \ ((xcoord) >= (rect).x && \ (xcoord) < ((rect).x + (rect).width) && \ (ycoord) >= (rect).y && \ (ycoord) < ((rect).y + (rect).height)) static void wnck_pager_finalize (GObject *object); static void wnck_pager_realize (GtkWidget *widget); static void wnck_pager_unrealize (GtkWidget *widget); static GtkSizeRequestMode wnck_pager_get_request_mode (GtkWidget *widget); static void wnck_pager_get_preferred_width (GtkWidget *widget, int *minimum_width, int *natural_width); static void wnck_pager_get_preferred_width_for_height (GtkWidget *widget, int height, int *minimum_width, int *natural_width); static void wnck_pager_get_preferred_height (GtkWidget *widget, int *minimum_height, int *natural_height); static void wnck_pager_get_preferred_height_for_width (GtkWidget *widget, int width, int *minimum_height, int *natural_height); static gboolean wnck_pager_draw (GtkWidget *widget, cairo_t *cr); static gboolean wnck_pager_button_press (GtkWidget *widget, GdkEventButton *event); static gboolean wnck_pager_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time); static void wnck_pager_drag_motion_leave (GtkWidget *widget, GdkDragContext *context, guint time); static gboolean wnck_pager_drag_drop (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time); static void wnck_pager_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time_); static void wnck_pager_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time); static void wnck_pager_drag_end (GtkWidget *widget, GdkDragContext *context); static gboolean wnck_pager_motion (GtkWidget *widget, GdkEventMotion *event); static gboolean wnck_pager_leave_notify (GtkWidget *widget, GdkEventCrossing *event); static gboolean wnck_pager_button_release (GtkWidget *widget, GdkEventButton *event); static gboolean wnck_pager_scroll_event (GtkWidget *widget, GdkEventScroll *event); static gboolean wnck_pager_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip); static void workspace_name_changed_callback (WnckWorkspace *workspace, gpointer data); static gboolean wnck_pager_window_state_is_relevant (int state); static gint wnck_pager_window_get_workspace (WnckWindow *window, gboolean is_state_relevant); static void wnck_pager_queue_draw_workspace (WnckPager *pager, gint i); static void wnck_pager_queue_draw_window (WnckPager *pager, WnckWindow *window); static void wnck_pager_connect_screen (WnckPager *pager); static void wnck_pager_connect_window (WnckPager *pager, WnckWindow *window); static void wnck_pager_disconnect_screen (WnckPager *pager); static void wnck_pager_disconnect_window (WnckPager *pager, WnckWindow *window); static gboolean wnck_pager_set_layout_hint (WnckPager *pager); static void wnck_pager_clear_drag (WnckPager *pager); static void wnck_pager_check_prelight (WnckPager *pager, gint x, gint y, gboolean dnd); static GdkPixbuf* wnck_pager_get_background (WnckPager *pager, int width, int height); static AtkObject* wnck_pager_get_accessible (GtkWidget *widget); static void wnck_pager_init (WnckPager *pager) { int i; static const GtkTargetEntry targets[] = { { (gchar *) "application/x-wnck-window-id", 0, 0} }; pager->priv = wnck_pager_get_instance_private (pager); pager->priv->n_rows = 1; pager->priv->display_mode = WNCK_PAGER_DISPLAY_CONTENT; pager->priv->scroll_mode = WNCK_PAGER_SCROLL_2D; pager->priv->show_all_workspaces = TRUE; pager->priv->shadow_type = GTK_SHADOW_NONE; pager->priv->wrap_on_scroll = FALSE; pager->priv->orientation = GTK_ORIENTATION_HORIZONTAL; pager->priv->workspace_size = 16; for (i = 0; i < N_SCREEN_CONNECTIONS; i++) pager->priv->screen_connections[i] = 0; pager->priv->prelight = -1; pager->priv->layout_manager_token = WNCK_NO_MANAGER_TOKEN; g_object_set (pager, "has-tooltip", TRUE, NULL); gtk_drag_dest_set (GTK_WIDGET (pager), 0, targets, G_N_ELEMENTS (targets), GDK_ACTION_MOVE); gtk_widget_set_can_focus (GTK_WIDGET (pager), TRUE); } static void wnck_pager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WnckPager *self; self = WNCK_PAGER (object); switch (property_id) { case PROP_HANDLE: g_value_set_object (value, self->priv->handle); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wnck_pager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WnckPager *self; self = WNCK_PAGER (object); switch (property_id) { case PROP_HANDLE: g_assert (self->priv->handle == NULL); self->priv->handle = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void install_properties (GObjectClass *object_class) { pager_properties[PROP_HANDLE] = g_param_spec_object ("handle", "handle", "handle", WNCK_TYPE_HANDLE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, LAST_PROP, pager_properties); } static void wnck_pager_class_init (WnckPagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->finalize = wnck_pager_finalize; object_class->get_property = wnck_pager_get_property; object_class->set_property = wnck_pager_set_property; widget_class->realize = wnck_pager_realize; widget_class->unrealize = wnck_pager_unrealize; widget_class->get_request_mode = wnck_pager_get_request_mode; widget_class->get_preferred_width = wnck_pager_get_preferred_width; widget_class->get_preferred_width_for_height = wnck_pager_get_preferred_width_for_height; widget_class->get_preferred_height = wnck_pager_get_preferred_height; widget_class->get_preferred_height_for_width = wnck_pager_get_preferred_height_for_width; widget_class->draw = wnck_pager_draw; widget_class->button_press_event = wnck_pager_button_press; widget_class->button_release_event = wnck_pager_button_release; widget_class->scroll_event = wnck_pager_scroll_event; widget_class->motion_notify_event = wnck_pager_motion; widget_class->leave_notify_event = wnck_pager_leave_notify; widget_class->get_accessible = wnck_pager_get_accessible; widget_class->drag_leave = wnck_pager_drag_motion_leave; widget_class->drag_motion = wnck_pager_drag_motion; widget_class->drag_drop = wnck_pager_drag_drop; widget_class->drag_data_received = wnck_pager_drag_data_received; widget_class->drag_data_get = wnck_pager_drag_data_get; widget_class->drag_end = wnck_pager_drag_end; widget_class->query_tooltip = wnck_pager_query_tooltip; install_properties (object_class); gtk_widget_class_set_css_name (widget_class, "wnck-pager"); } static void wnck_pager_finalize (GObject *object) { WnckPager *pager; pager = WNCK_PAGER (object); if (pager->priv->bg_cache) { g_object_unref (G_OBJECT (pager->priv->bg_cache)); pager->priv->bg_cache = NULL; } if (pager->priv->dnd_activate != 0) { g_source_remove (pager->priv->dnd_activate); pager->priv->dnd_activate = 0; } g_clear_object (&pager->priv->handle); G_OBJECT_CLASS (wnck_pager_parent_class)->finalize (object); } static void _wnck_pager_set_screen (WnckPager *pager) { GdkScreen *gdkscreen; int screen_number; if (!gtk_widget_has_screen (GTK_WIDGET (pager))) return; gdkscreen = gtk_widget_get_screen (GTK_WIDGET (pager)); screen_number = gdk_x11_screen_get_screen_number (gdkscreen); pager->priv->screen = wnck_handle_get_screen (pager->priv->handle, screen_number); if (!wnck_pager_set_layout_hint (pager)) { _WnckLayoutOrientation orientation; /* we couldn't set the layout on the screen. This means someone else owns * it. Let's at least show the correct layout. */ _wnck_screen_get_workspace_layout (pager->priv->screen, &orientation, &pager->priv->n_rows, NULL, NULL); /* test in this order to default to horizontal in case there was in issue * when fetching the layout */ if (orientation == WNCK_LAYOUT_ORIENTATION_VERTICAL) pager->priv->orientation = GTK_ORIENTATION_VERTICAL; else pager->priv->orientation = GTK_ORIENTATION_HORIZONTAL; gtk_widget_queue_resize (GTK_WIDGET (pager)); } wnck_pager_connect_screen (pager); } static void wnck_pager_realize (GtkWidget *widget) { GdkWindowAttr attributes; gint attributes_mask; WnckPager *pager; GtkAllocation allocation; GdkWindow *window; pager = WNCK_PAGER (widget); /* do not call the parent class realize since we're doing things a bit * differently here */ gtk_widget_set_realized (widget, TRUE); gtk_widget_get_allocation (widget, &allocation); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = allocation.x; attributes.y = allocation.y; attributes.width = allocation.width; attributes.height = allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); gdk_window_set_user_data (window, widget); /* connect to the screen of this pager. In theory, this will already have * been done in wnck_pager_size_request() */ if (pager->priv->screen == NULL) _wnck_pager_set_screen (pager); g_assert (pager->priv->screen != NULL); } static void wnck_pager_unrealize (GtkWidget *widget) { WnckPager *pager; pager = WNCK_PAGER (widget); wnck_pager_clear_drag (pager); pager->priv->prelight = -1; pager->priv->prelight_dnd = FALSE; wnck_screen_release_workspace_layout (pager->priv->screen, pager->priv->layout_manager_token); pager->priv->layout_manager_token = WNCK_NO_MANAGER_TOKEN; wnck_pager_disconnect_screen (pager); pager->priv->screen = NULL; GTK_WIDGET_CLASS (wnck_pager_parent_class)->unrealize (widget); } static void _wnck_pager_get_padding (WnckPager *pager, GtkBorder *padding) { if (pager->priv->shadow_type != GTK_SHADOW_NONE) { GtkWidget *widget; GtkStyleContext *context; GtkStateFlags state; widget = GTK_WIDGET (pager); context = gtk_widget_get_style_context (widget); state = gtk_style_context_get_state (context); gtk_style_context_get_padding (context, state, padding); } else { GtkBorder empty_padding = { 0, 0, 0, 0 }; *padding = empty_padding; } } static int _wnck_pager_get_workspace_width_for_height (WnckPager *pager, int workspace_height) { int workspace_width = 0; if (pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT) { WnckWorkspace *space; double screen_aspect; space = wnck_screen_get_workspace (pager->priv->screen, 0); if (space) { screen_aspect = (double) wnck_workspace_get_width (space) / (double) wnck_workspace_get_height (space); } else { screen_aspect = (double) wnck_screen_get_width (pager->priv->screen) / (double) wnck_screen_get_height (pager->priv->screen); } workspace_width = screen_aspect * workspace_height; } else { PangoLayout *layout; WnckScreen *screen; int n_spaces; int i, w; layout = gtk_widget_create_pango_layout (GTK_WIDGET (pager), NULL); screen = pager->priv->screen; n_spaces = wnck_screen_get_workspace_count (pager->priv->screen); workspace_width = 1; for (i = 0; i < n_spaces; i++) { pango_layout_set_text (layout, wnck_workspace_get_name (wnck_screen_get_workspace (screen, i)), -1); pango_layout_get_pixel_size (layout, &w, NULL); workspace_width = MAX (workspace_width, w); } g_object_unref (layout); workspace_width += 2; } return workspace_width; } static int _wnck_pager_get_workspace_height_for_width (WnckPager *pager, int workspace_width) { int workspace_height = 0; WnckWorkspace *space; double screen_aspect; /* TODO: Handle WNCK_PAGER_DISPLAY_NAME for this case */ space = wnck_screen_get_workspace (pager->priv->screen, 0); if (space) { screen_aspect = (double) wnck_workspace_get_height (space) / (double) wnck_workspace_get_width (space); } else { screen_aspect = (double) wnck_screen_get_height (pager->priv->screen) / (double) wnck_screen_get_width (pager->priv->screen); } workspace_height = screen_aspect * workspace_width; return workspace_height; } static void wnck_pager_size_request (GtkWidget *widget, GtkRequisition *requisition) { WnckPager *pager; int n_spaces; int spaces_per_row; int workspace_width, workspace_height; int n_rows; GtkBorder padding; pager = WNCK_PAGER (widget); /* if we're not realized, we don't know about our screen yet */ if (pager->priv->screen == NULL) _wnck_pager_set_screen (pager); g_assert (pager->priv->screen != NULL); g_assert (pager->priv->n_rows > 0); n_spaces = wnck_screen_get_workspace_count (pager->priv->screen); if (pager->priv->show_all_workspaces) { n_rows = pager->priv->n_rows; spaces_per_row = (n_spaces + n_rows - 1) / n_rows; } else { n_rows = 1; spaces_per_row = 1; } if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL) { workspace_width = pager->priv->workspace_size; workspace_height = _wnck_pager_get_workspace_height_for_width (pager, workspace_width); requisition->width = workspace_width * n_rows + (n_rows - 1); requisition->height = workspace_height * spaces_per_row + (spaces_per_row - 1); } else { workspace_height = pager->priv->workspace_size; workspace_width = _wnck_pager_get_workspace_width_for_height (pager, workspace_height); requisition->width = workspace_width * spaces_per_row + (spaces_per_row - 1); requisition->height = workspace_height * n_rows + (n_rows - 1); } _wnck_pager_get_padding (pager, &padding); requisition->width += padding.left + padding.right; requisition->height += padding.top + padding.bottom; } static GtkSizeRequestMode wnck_pager_get_request_mode (GtkWidget *widget) { WnckPager *pager; pager = WNCK_PAGER (widget); if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL) return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; else return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT; } static void wnck_pager_get_preferred_width (GtkWidget *widget, int *minimum_width, int *natural_width) { GtkRequisition req; wnck_pager_size_request (widget, &req); *minimum_width = *natural_width = MAX (req.width, 0); } static void wnck_pager_get_preferred_width_for_height (GtkWidget *widget, int height, int *minimum_width, int *natural_width) { WnckPager *pager; int n_spaces; int n_rows; int spaces_per_row; int workspace_width, workspace_height; GtkBorder padding; int width = 0; pager = WNCK_PAGER (widget); /* if we're not realized, we don't know about our screen yet */ if (pager->priv->screen == NULL) _wnck_pager_set_screen (pager); g_assert (pager->priv->screen != NULL); g_assert (pager->priv->n_rows > 0); n_spaces = wnck_screen_get_workspace_count (pager->priv->screen); if (pager->priv->show_all_workspaces) { n_rows = pager->priv->n_rows; spaces_per_row = (n_spaces + n_rows - 1) / n_rows; } else { n_rows = 1; spaces_per_row = 1; } _wnck_pager_get_padding (pager, &padding); height -= padding.top + padding.bottom; width += padding.left + padding.right; height -= (n_rows - 1); workspace_height = height / n_rows; workspace_width = _wnck_pager_get_workspace_width_for_height (pager, workspace_height); width += workspace_width * spaces_per_row + (spaces_per_row - 1); *natural_width = *minimum_width = MAX (width, 0); } static void wnck_pager_get_preferred_height (GtkWidget *widget, int *minimum_height, int *natural_height) { GtkRequisition req; wnck_pager_size_request (widget, &req); *minimum_height = *natural_height = MAX (req.height, 0); } static void wnck_pager_get_preferred_height_for_width (GtkWidget *widget, int width, int *minimum_height, int *natural_height) { WnckPager *pager; int n_spaces; int n_rows; int spaces_per_row; int workspace_width, workspace_height; GtkBorder padding; int height = 0; pager = WNCK_PAGER (widget); /* if we're not realized, we don't know about our screen yet */ if (pager->priv->screen == NULL) _wnck_pager_set_screen (pager); g_assert (pager->priv->screen != NULL); g_assert (pager->priv->n_rows > 0); n_spaces = wnck_screen_get_workspace_count (pager->priv->screen); if (pager->priv->show_all_workspaces) { n_rows = pager->priv->n_rows; spaces_per_row = (n_spaces + n_rows - 1) / n_rows; } else { n_rows = 1; spaces_per_row = 1; } _wnck_pager_get_padding (pager, &padding); width -= padding.left + padding.right; height += padding.top + padding.bottom; width -= (n_rows - 1); workspace_width = width / n_rows; workspace_height = _wnck_pager_get_workspace_height_for_width (pager, workspace_width); height += workspace_height * spaces_per_row + (spaces_per_row - 1); *natural_height = *minimum_height = MAX (height, 0); } static void get_workspace_rect (WnckPager *pager, int space, GdkRectangle *rect) { int hsize, vsize; int n_spaces; int spaces_per_row; GtkWidget *widget; int col, row; GtkAllocation allocation; GtkBorder padding; widget = GTK_WIDGET (pager); gtk_widget_get_allocation (widget, &allocation); if (allocation.x < 0 || allocation.y < 0 || allocation.width < 0 || allocation.height < 0) { rect->x = 0; rect->y = 0; rect->width = 0; rect->height = 0; return; } _wnck_pager_get_padding (pager, &padding); if (!pager->priv->show_all_workspaces) { WnckWorkspace *active_space; active_space = wnck_screen_get_active_workspace (pager->priv->screen); if (active_space && space == wnck_workspace_get_number (active_space)) { rect->x = padding.left; rect->y = padding.top; rect->width = allocation.width - padding.left - padding.right; rect->height = allocation.height - padding.top - padding.bottom; } else { rect->x = 0; rect->y = 0; rect->width = 0; rect->height = 0; } return; } hsize = allocation.width; vsize = allocation.height; if (pager->priv->shadow_type != GTK_SHADOW_NONE) { hsize -= padding.left + padding.right; vsize -= padding.top + padding.bottom; } n_spaces = wnck_screen_get_workspace_count (pager->priv->screen); g_assert (pager->priv->n_rows > 0); spaces_per_row = (n_spaces + pager->priv->n_rows - 1) / pager->priv->n_rows; if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL) { rect->width = (hsize - (pager->priv->n_rows - 1)) / pager->priv->n_rows; rect->height = (vsize - (spaces_per_row - 1)) / spaces_per_row; col = space / spaces_per_row; row = space % spaces_per_row; if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) col = pager->priv->n_rows - col - 1; rect->x = (rect->width + 1) * col; rect->y = (rect->height + 1) * row; if (col == pager->priv->n_rows - 1) rect->width = hsize - rect->x; if (row == spaces_per_row - 1) rect->height = vsize - rect->y; } else { rect->width = (hsize - (spaces_per_row - 1)) / spaces_per_row; rect->height = (vsize - (pager->priv->n_rows - 1)) / pager->priv->n_rows; col = space % spaces_per_row; row = space / spaces_per_row; if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) col = spaces_per_row - col - 1; rect->x = (rect->width + 1) * col; rect->y = (rect->height + 1) * row; if (col == spaces_per_row - 1) rect->width = hsize - rect->x; if (row == pager->priv->n_rows - 1) rect->height = vsize - rect->y; } if (pager->priv->shadow_type != GTK_SHADOW_NONE) { rect->x += padding.left; rect->y += padding.top; } } static gboolean wnck_pager_window_state_is_relevant (int state) { return (state & (WNCK_WINDOW_STATE_HIDDEN | WNCK_WINDOW_STATE_SKIP_PAGER)) ? FALSE : TRUE; } static gint wnck_pager_window_get_workspace (WnckWindow *window, gboolean is_state_relevant) { gint state; WnckWorkspace *workspace; state = wnck_window_get_state (window); if (is_state_relevant && !wnck_pager_window_state_is_relevant (state)) return -1; workspace = wnck_window_get_workspace (window); if (workspace == NULL && wnck_window_is_pinned (window)) workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window)); return workspace ? wnck_workspace_get_number (workspace) : -1; } static GList* get_windows_for_workspace_in_bottom_to_top (WnckScreen *screen, WnckWorkspace *workspace) { GList *result; GList *windows; GList *tmp; int workspace_num; result = NULL; workspace_num = wnck_workspace_get_number (workspace); windows = wnck_screen_get_windows_stacked (screen); for (tmp = windows; tmp != NULL; tmp = tmp->next) { WnckWindow *win = WNCK_WINDOW (tmp->data); if (wnck_pager_window_get_workspace (win, TRUE) == workspace_num) result = g_list_prepend (result, win); } result = g_list_reverse (result); return result; } static void get_window_rect (WnckWindow *window, const GdkRectangle *workspace_rect, GdkRectangle *rect) { double width_ratio, height_ratio; int x, y, width, height; WnckWorkspace *workspace; GdkRectangle unclipped_win_rect; workspace = wnck_window_get_workspace (window); if (workspace == NULL) workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window)); /* scale window down by same ratio we scaled workspace down */ width_ratio = (double) workspace_rect->width / (double) wnck_workspace_get_width (workspace); height_ratio = (double) workspace_rect->height / (double) wnck_workspace_get_height (workspace); wnck_window_get_geometry (window, &x, &y, &width, &height); x += wnck_workspace_get_viewport_x (workspace); y += wnck_workspace_get_viewport_y (workspace); x = x * width_ratio + 0.5; y = y * height_ratio + 0.5; width = width * width_ratio + 0.5; height = height * height_ratio + 0.5; x += workspace_rect->x; y += workspace_rect->y; if (width < 3) width = 3; if (height < 3) height = 3; unclipped_win_rect.x = x; unclipped_win_rect.y = y; unclipped_win_rect.width = width; unclipped_win_rect.height = height; gdk_rectangle_intersect ((GdkRectangle *) workspace_rect, &unclipped_win_rect, rect); } static void draw_window (cairo_t *cr, GtkWidget *widget, WnckWindow *win, const GdkRectangle *winrect, GtkStateFlags state, gboolean translucent) { GtkStyleContext *context; GdkPixbuf *icon; int icon_x, icon_y, icon_w, icon_h; gboolean is_active; GdkRGBA fg; gdouble translucency; context = gtk_widget_get_style_context (widget); is_active = wnck_window_is_active (win); translucency = translucent ? 0.4 : 1.0; gtk_style_context_save (context); gtk_style_context_set_state (context, state); cairo_push_group (cr); gtk_render_background (context, cr, winrect->x + 1, winrect->y + 1, MAX (0, winrect->width - 2), MAX (0, winrect->height - 2)); if (is_active) { /* Sharpen the foreground color */ cairo_set_source_rgba (cr, 1.0f, 1.0f, 1.0f, 0.3f); cairo_rectangle (cr, winrect->x + 1, winrect->y + 1, MAX (0, winrect->width - 2), MAX (0, winrect->height - 2)); cairo_fill (cr); } cairo_pop_group_to_source (cr); cairo_paint_with_alpha (cr, translucency); icon = wnck_window_get_icon (win); icon_w = icon_h = 0; if (icon) { icon_w = gdk_pixbuf_get_width (icon); icon_h = gdk_pixbuf_get_height (icon); /* If the icon is too big, fall back to mini icon. * We don't arbitrarily scale the icon, because it's * just too slow on my Athlon 850. */ if (icon_w > (winrect->width - 2) || icon_h > (winrect->height - 2)) { icon = wnck_window_get_mini_icon (win); if (icon) { icon_w = gdk_pixbuf_get_width (icon); icon_h = gdk_pixbuf_get_height (icon); /* Give up. */ if (icon_w > (winrect->width - 2) || icon_h > (winrect->height - 2)) icon = NULL; } } } if (icon) { icon_x = winrect->x + (winrect->width - icon_w) / 2; icon_y = winrect->y + (winrect->height - icon_h) / 2; cairo_push_group (cr); gtk_render_icon (context, cr, icon, icon_x, icon_y); cairo_pop_group_to_source (cr); cairo_paint_with_alpha (cr, translucency); } cairo_push_group (cr); gtk_render_frame (context, cr, winrect->x + 0.5, winrect->y + 0.5, MAX (0, winrect->width - 1), MAX (0, winrect->height - 1)); cairo_pop_group_to_source (cr); cairo_paint_with_alpha (cr, translucency); gtk_style_context_get_color (context, state, &fg); fg.alpha = translucency; gdk_cairo_set_source_rgba (cr, &fg); cairo_set_line_width (cr, 1.0); cairo_rectangle (cr, winrect->x + 0.5, winrect->y + 0.5, MAX (0, winrect->width - 1), MAX (0, winrect->height - 1)); cairo_stroke (cr); gtk_style_context_restore (context); } static WnckWindow * window_at_point (WnckPager *pager, WnckWorkspace *space, GdkRectangle *space_rect, int x, int y) { WnckWindow *window; GList *windows; GList *tmp; window = NULL; windows = get_windows_for_workspace_in_bottom_to_top (pager->priv->screen, space); /* clicks on top windows first */ windows = g_list_reverse (windows); for (tmp = windows; tmp != NULL; tmp = tmp->next) { WnckWindow *win = WNCK_WINDOW (tmp->data); GdkRectangle winrect; get_window_rect (win, space_rect, &winrect); if (POINT_IN_RECT (x, y, winrect)) { /* wnck_window_activate (win); */ window = win; break; } } g_list_free (windows); return window; } static int workspace_at_point (WnckPager *pager, int x, int y, int *viewport_x, int *viewport_y) { GtkWidget *widget; int i; int n_spaces; GtkAllocation allocation; GtkBorder padding; widget = GTK_WIDGET (pager); gtk_widget_get_allocation (widget, &allocation); _wnck_pager_get_padding (pager, &padding); n_spaces = wnck_screen_get_workspace_count (pager->priv->screen); i = 0; while (i < n_spaces) { GdkRectangle rect; get_workspace_rect (pager, i, &rect); /* If workspace is on the edge, pretend points on the frame belong to the * workspace. * Else, pretend the right/bottom line separating two workspaces belong * to the workspace. */ if (rect.x == padding.left) { rect.x = 0; rect.width += padding.left; } if (rect.y == padding.top) { rect.y = 0; rect.height += padding.top; } if (rect.y + rect.height == allocation.height - padding.bottom) { rect.height += padding.bottom; } else { rect.height += 1; } if (rect.x + rect.width == allocation.width - padding.right) { rect.width += padding.right; } else { rect.width += 1; } if (POINT_IN_RECT (x, y, rect)) { double width_ratio, height_ratio; WnckWorkspace *space; space = wnck_screen_get_workspace (pager->priv->screen, i); g_assert (space != NULL); /* Scale x, y mouse coords to corresponding screenwide viewport coords */ width_ratio = (double) wnck_workspace_get_width (space) / (double) rect.width; height_ratio = (double) wnck_workspace_get_height (space) / (double) rect.height; if (viewport_x) *viewport_x = width_ratio * (x - rect.x); if (viewport_y) *viewport_y = height_ratio * (y - rect.y); return i; } ++i; } return -1; } static void draw_dark_rectangle (GtkStyleContext *context, cairo_t *cr, GtkStateFlags state, int rx, int ry, int rw, int rh) { gtk_style_context_save (context); gtk_style_context_set_state (context, state); cairo_push_group (cr); gtk_render_background (context, cr, rx, ry, rw, rh); cairo_set_source_rgba (cr, 0.0f, 0.0f, 0.0f, 0.3f); cairo_rectangle (cr, rx, ry, rw, rh); cairo_fill (cr); cairo_pop_group_to_source (cr); cairo_paint (cr); gtk_style_context_restore (context); } static void wnck_pager_draw_workspace (WnckPager *pager, cairo_t *cr, int workspace, GdkRectangle *rect, GdkPixbuf *bg_pixbuf) { GList *windows; GList *tmp; gboolean is_current; WnckWorkspace *space; GtkWidget *widget; GtkStateFlags state; GtkStyleContext *context; space = wnck_screen_get_workspace (pager->priv->screen, workspace); if (!space) return; widget = GTK_WIDGET (pager); is_current = (space == wnck_screen_get_active_workspace (pager->priv->screen)); state = GTK_STATE_FLAG_NORMAL; if (is_current) state |= GTK_STATE_FLAG_SELECTED; else if (workspace == pager->priv->prelight) state |= GTK_STATE_FLAG_PRELIGHT; context = gtk_widget_get_style_context (widget); /* FIXME in names mode, should probably draw things like a button. */ if (bg_pixbuf) { gdk_cairo_set_source_pixbuf (cr, bg_pixbuf, rect->x, rect->y); cairo_paint (cr); } else { if (!wnck_workspace_is_virtual (space)) { draw_dark_rectangle (context, cr, state, rect->x, rect->y, rect->width, rect->height); } else { //FIXME prelight for dnd in the viewport? int workspace_width, workspace_height; int screen_width, screen_height; double width_ratio, height_ratio; double vx, vy, vw, vh; /* viewport */ workspace_width = wnck_workspace_get_width (space); workspace_height = wnck_workspace_get_height (space); screen_width = wnck_screen_get_width (pager->priv->screen); screen_height = wnck_screen_get_height (pager->priv->screen); if ((workspace_width % screen_width == 0) && (workspace_height % screen_height == 0)) { int i, j; int active_i, active_j; int horiz_views; int verti_views; horiz_views = workspace_width / screen_width; verti_views = workspace_height / screen_height; /* do not forget thin lines to delimit "workspaces" */ width_ratio = (rect->width - (horiz_views - 1)) / (double) workspace_width; height_ratio = (rect->height - (verti_views - 1)) / (double) workspace_height; if (is_current) { active_i = wnck_workspace_get_viewport_x (space) / screen_width; active_j = wnck_workspace_get_viewport_y (space) / screen_height; } else { active_i = -1; active_j = -1; } for (i = 0; i < horiz_views; i++) { /* "+ i" is for the thin lines */ vx = rect->x + (width_ratio * screen_width) * i + i; if (i == horiz_views - 1) vw = rect->width + rect->x - vx; else vw = width_ratio * screen_width; vh = height_ratio * screen_height; for (j = 0; j < verti_views; j++) { GtkStateFlags rec_state = GTK_STATE_FLAG_NORMAL; /* "+ j" is for the thin lines */ vy = rect->y + (height_ratio * screen_height) * j + j; if (j == verti_views - 1) vh = rect->height + rect->y - vy; if (active_i == i && active_j == j) rec_state = GTK_STATE_FLAG_SELECTED; draw_dark_rectangle (context, cr, rec_state, vx, vy, vw, vh); } } } else { width_ratio = rect->width / (double) workspace_width; height_ratio = rect->height / (double) workspace_height; /* first draw non-active part of the viewport */ draw_dark_rectangle (context, cr, GTK_STATE_FLAG_NORMAL, rect->x, rect->y, rect->width, rect->height); if (is_current) { /* draw the active part of the viewport */ vx = rect->x + width_ratio * wnck_workspace_get_viewport_x (space); vy = rect->y + height_ratio * wnck_workspace_get_viewport_y (space); vw = width_ratio * screen_width; vh = height_ratio * screen_height; draw_dark_rectangle (context, cr, GTK_STATE_FLAG_SELECTED, vx, vy, vw, vh); } } } } if (pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT) { windows = get_windows_for_workspace_in_bottom_to_top (pager->priv->screen, wnck_screen_get_workspace (pager->priv->screen, workspace)); tmp = windows; while (tmp != NULL) { WnckWindow *win = tmp->data; GdkRectangle winrect; gboolean translucent; get_window_rect (win, rect, &winrect); translucent = (win == pager->priv->drag_window) && pager->priv->dragging; draw_window (cr, widget, win, &winrect, state, translucent); tmp = tmp->next; } g_list_free (windows); } else { /* Workspace name mode */ GtkStateFlags layout_state; const char *workspace_name; PangoLayout *layout; WnckWorkspace *ws; int w, h; ws = wnck_screen_get_workspace (pager->priv->screen, workspace); workspace_name = wnck_workspace_get_name (ws); layout = gtk_widget_create_pango_layout (widget, workspace_name); pango_layout_get_pixel_size (layout, &w, &h); layout_state = (is_current) ? GTK_STATE_FLAG_SELECTED : GTK_STATE_FLAG_NORMAL; gtk_style_context_save (context); gtk_style_context_set_state (context, layout_state); gtk_render_layout (context, cr, rect->x + (rect->width - w) / 2, rect->y + (rect->height - h) / 2, layout); gtk_style_context_restore (context); g_object_unref (layout); } if (workspace == pager->priv->prelight && pager->priv->prelight_dnd) { gtk_style_context_save (context); gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL); gtk_render_frame (context, cr, rect->x, rect->y, rect->width, rect->height); gtk_style_context_restore (context); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ cairo_set_line_width (cr, 1.0); cairo_rectangle (cr, rect->x + 0.5, rect->y + 0.5, MAX (0, rect->width - 1), MAX (0, rect->height - 1)); cairo_stroke (cr); } } static gboolean wnck_pager_draw (GtkWidget *widget, cairo_t *cr) { WnckPager *pager; int i; int n_spaces; WnckWorkspace *active_space; GdkPixbuf *bg_pixbuf; gboolean first; GtkStyleContext *context; GtkStateFlags state; pager = WNCK_PAGER (widget); n_spaces = wnck_screen_get_workspace_count (pager->priv->screen); active_space = wnck_screen_get_active_workspace (pager->priv->screen); bg_pixbuf = NULL; first = TRUE; state = gtk_widget_get_state_flags (widget); context = gtk_widget_get_style_context (widget); gtk_render_background (context, cr, 0, 0, gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget)); gtk_style_context_save (context); gtk_style_context_set_state (context, state); if (gtk_widget_has_focus (widget)) { cairo_save (cr); gtk_render_focus (context, cr, 0, 0, gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget)); cairo_restore (cr); } if (pager->priv->shadow_type != GTK_SHADOW_NONE) { cairo_save (cr); gtk_render_frame (context, cr, 0, 0, gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget)); cairo_restore (cr); } gtk_style_context_restore (context); i = 0; while (i < n_spaces) { GdkRectangle rect; if (pager->priv->show_all_workspaces || (active_space && i == wnck_workspace_get_number (active_space))) { get_workspace_rect (pager, i, &rect); /* We only want to do this once, even if w/h change, * for efficiency. width/height will only change by * one pixel at most. */ if (first && pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT) { bg_pixbuf = wnck_pager_get_background (pager, rect.width, rect.height); first = FALSE; } wnck_pager_draw_workspace (pager, cr, i, &rect, bg_pixbuf); } ++i; } return FALSE; } static gboolean wnck_pager_button_press (GtkWidget *widget, GdkEventButton *event) { WnckPager *pager; int space_number; WnckWorkspace *space = NULL; GdkRectangle workspace_rect; if (event->button != 1) return FALSE; pager = WNCK_PAGER (widget); space_number = workspace_at_point (pager, event->x, event->y, NULL, NULL); if (space_number != -1) { get_workspace_rect (pager, space_number, &workspace_rect); space = wnck_screen_get_workspace (pager->priv->screen, space_number); } if (space) { /* always save the start coordinates so we can know if we need to change * workspace when the button is released (ie, start and end coordinates * should be in the same workspace) */ pager->priv->drag_start_x = event->x; pager->priv->drag_start_y = event->y; } if (space && (pager->priv->display_mode != WNCK_PAGER_DISPLAY_NAME)) { pager->priv->drag_window = window_at_point (pager, space, &workspace_rect, event->x, event->y); } return TRUE; } static gboolean wnck_pager_drag_motion_timeout (gpointer data) { WnckPager *pager = WNCK_PAGER (data); WnckWorkspace *active_workspace, *dnd_workspace; pager->priv->dnd_activate = 0; active_workspace = wnck_screen_get_active_workspace (pager->priv->screen); dnd_workspace = wnck_screen_get_workspace (pager->priv->screen, pager->priv->prelight); if (dnd_workspace && (pager->priv->prelight != wnck_workspace_get_number (active_workspace))) wnck_workspace_activate (dnd_workspace, pager->priv->dnd_time); return FALSE; } static void wnck_pager_queue_draw_workspace (WnckPager *pager, gint i) { GdkRectangle rect; if (i < 0) return; get_workspace_rect (pager, i, &rect); gtk_widget_queue_draw_area (GTK_WIDGET (pager), rect.x, rect.y, rect.width, rect.height); } static void wnck_pager_queue_draw_window (WnckPager *pager, WnckWindow *window) { gint workspace; workspace = wnck_pager_window_get_workspace (window, TRUE); if (workspace == -1) return; wnck_pager_queue_draw_workspace (pager, workspace); } static void wnck_pager_check_prelight (WnckPager *pager, gint x, gint y, gboolean prelight_dnd) { gint id; if (x < 0 || y < 0) id = -1; else id = workspace_at_point (pager, x, y, NULL, NULL); if (id != pager->priv->prelight) { wnck_pager_queue_draw_workspace (pager, pager->priv->prelight); wnck_pager_queue_draw_workspace (pager, id); pager->priv->prelight = id; pager->priv->prelight_dnd = prelight_dnd; } else if (prelight_dnd != pager->priv->prelight_dnd) { wnck_pager_queue_draw_workspace (pager, pager->priv->prelight); pager->priv->prelight_dnd = prelight_dnd; } } static gboolean wnck_pager_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { WnckPager *pager; gint previous_workspace; pager = WNCK_PAGER (widget); previous_workspace = pager->priv->prelight; wnck_pager_check_prelight (pager, x, y, TRUE); if (gtk_drag_dest_find_target (widget, context, NULL)) { gdk_drag_status (context, gdk_drag_context_get_suggested_action (context), time); } else { gdk_drag_status (context, 0, time); if (pager->priv->prelight != previous_workspace && pager->priv->dnd_activate != 0) { /* remove timeout, the window we hover over changed */ g_source_remove (pager->priv->dnd_activate); pager->priv->dnd_activate = 0; pager->priv->dnd_time = 0; } if (pager->priv->dnd_activate == 0 && pager->priv->prelight > -1) { pager->priv->dnd_activate = g_timeout_add_seconds (WNCK_ACTIVATE_TIMEOUT, wnck_pager_drag_motion_timeout, pager); pager->priv->dnd_time = time; } } return (pager->priv->prelight != -1); } static gboolean wnck_pager_drag_drop (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { WnckPager *pager = WNCK_PAGER (widget); GdkAtom target; target = gtk_drag_dest_find_target (widget, context, NULL); if (target != GDK_NONE) gtk_drag_get_data (widget, context, target, time); else gtk_drag_finish (context, FALSE, FALSE, time); wnck_pager_clear_drag (pager); wnck_pager_check_prelight (pager, x, y, FALSE); return TRUE; } static void wnck_pager_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time) { WnckPager *pager = WNCK_PAGER (widget); WnckWorkspace *space; GList *tmp; gint i; gulong xid; if ((gtk_selection_data_get_length (selection_data) != sizeof (gulong)) || (gtk_selection_data_get_format (selection_data) != 8)) { gtk_drag_finish (context, FALSE, FALSE, time); return; } i = workspace_at_point (pager, x, y, NULL, NULL); space = wnck_screen_get_workspace (pager->priv->screen, i); if (!space) { gtk_drag_finish (context, FALSE, FALSE, time); return; } xid = *((gulong *) gtk_selection_data_get_data (selection_data)); for (tmp = wnck_screen_get_windows_stacked (pager->priv->screen); tmp != NULL; tmp = tmp->next) { if (wnck_window_get_xid (tmp->data) == xid) { WnckWindow *win = tmp->data; wnck_window_move_to_workspace (win, space); if (space == wnck_screen_get_active_workspace (pager->priv->screen)) wnck_window_activate (win, time); gtk_drag_finish (context, TRUE, FALSE, time); return; } } gtk_drag_finish (context, FALSE, FALSE, time); } static void wnck_pager_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time) { WnckPager *pager = WNCK_PAGER (widget); gulong xid; if (pager->priv->drag_window == NULL) return; xid = wnck_window_get_xid (pager->priv->drag_window); gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *)&xid, sizeof (gulong)); } static void wnck_pager_drag_end (GtkWidget *widget, GdkDragContext *context) { WnckPager *pager = WNCK_PAGER (widget); wnck_pager_clear_drag (pager); } static void wnck_pager_drag_motion_leave (GtkWidget *widget, GdkDragContext *context, guint time) { WnckPager *pager = WNCK_PAGER (widget); if (pager->priv->dnd_activate != 0) { g_source_remove (pager->priv->dnd_activate); pager->priv->dnd_activate = 0; } pager->priv->dnd_time = 0; wnck_pager_check_prelight (pager, -1, -1, FALSE); } static void wnck_drag_clean_up (WnckWindow *window, GdkDragContext *context, gboolean clean_up_for_context_destroy, gboolean clean_up_for_window_destroy); static void wnck_drag_context_destroyed (gpointer windowp, GObject *context) { wnck_drag_clean_up (windowp, (GdkDragContext *) context, TRUE, FALSE); } static void wnck_update_drag_icon (WnckWindow *window, GdkDragContext *context) { gint org_w, org_h, dnd_w, dnd_h; WnckWorkspace *workspace; GdkRectangle rect; cairo_surface_t *surface; GtkWidget *widget; cairo_t *cr; widget = g_object_get_data (G_OBJECT (context), "wnck-drag-source-widget"); if (!widget) return; if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &dnd_w, &dnd_h)) dnd_w = dnd_h = 32; /* windows are huge, so let's make this huge */ dnd_w *= 3; workspace = wnck_window_get_workspace (window); if (workspace == NULL) workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window)); if (workspace == NULL) return; wnck_window_get_geometry (window, NULL, NULL, &org_w, &org_h); rect.x = rect.y = 0; rect.width = 0.5 + ((double) (dnd_w * org_w) / (double) wnck_workspace_get_width (workspace)); rect.width = MIN (org_w, rect.width); rect.height = 0.5 + ((double) (rect.width * org_h) / (double) org_w); /* we need at least three pixels to draw the smallest window */ rect.width = MAX (rect.width, 3); rect.height = MAX (rect.height, 3); surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget), CAIRO_CONTENT_COLOR, rect.width, rect.height); cr = cairo_create (surface); draw_window (cr, widget, window, &rect, GTK_STATE_FLAG_NORMAL, FALSE); cairo_destroy (cr); cairo_surface_set_device_offset (surface, 2, 2); gtk_drag_set_icon_surface (context, surface); cairo_surface_destroy (surface); } static void wnck_drag_window_destroyed (gpointer contextp, GObject *window) { wnck_drag_clean_up ((WnckWindow *) window, GDK_DRAG_CONTEXT (contextp), FALSE, TRUE); } static void wnck_drag_source_destroyed (gpointer contextp, GObject *drag_source) { g_object_steal_data (G_OBJECT (contextp), "wnck-drag-source-widget"); } /* CAREFUL: This function is a bit brittle, because the pointers given may be * finalized already */ static void wnck_drag_clean_up (WnckWindow *window, GdkDragContext *context, gboolean clean_up_for_context_destroy, gboolean clean_up_for_window_destroy) { if (clean_up_for_context_destroy) { GtkWidget *drag_source; drag_source = g_object_get_data (G_OBJECT (context), "wnck-drag-source-widget"); if (drag_source) g_object_weak_unref (G_OBJECT (drag_source), wnck_drag_source_destroyed, context); g_object_weak_unref (G_OBJECT (window), wnck_drag_window_destroyed, context); if (g_signal_handlers_disconnect_by_func (window, wnck_update_drag_icon, context) != 2) g_assert_not_reached (); } if (clean_up_for_window_destroy) { g_object_steal_data (G_OBJECT (context), "wnck-drag-source-widget"); g_object_weak_unref (G_OBJECT (context), wnck_drag_context_destroyed, window); } } /** * wnck_window_set_as_drag_icon: * @window: #WnckWindow to use as drag icon * @context: #GdkDragContext to set the icon on * @drag_source: #GtkWidget that started the drag, or one of its parent. This * widget needs to stay alive as long as possible during the drag. * * Sets the given @window as the drag icon for @context. **/ void _wnck_window_set_as_drag_icon (WnckWindow *window, GdkDragContext *context, GtkWidget *drag_source) { g_return_if_fail (WNCK_IS_WINDOW (window)); g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); g_object_weak_ref (G_OBJECT (window), wnck_drag_window_destroyed, context); g_signal_connect (window, "geometry_changed", G_CALLBACK (wnck_update_drag_icon), context); g_signal_connect (window, "icon_changed", G_CALLBACK (wnck_update_drag_icon), context); g_object_set_data (G_OBJECT (context), "wnck-drag-source-widget", drag_source); g_object_weak_ref (G_OBJECT (drag_source), wnck_drag_source_destroyed, context); g_object_weak_ref (G_OBJECT (context), wnck_drag_context_destroyed, window); wnck_update_drag_icon (window, context); } static gboolean wnck_pager_motion (GtkWidget *widget, GdkEventMotion *event) { WnckPager *pager; GdkWindow *window; GdkSeat *seat; GdkDevice *pointer; int x, y; pager = WNCK_PAGER (widget); seat = gdk_display_get_default_seat (gtk_widget_get_display (widget)); window = gtk_widget_get_window (widget); pointer = gdk_seat_get_pointer (seat); gdk_window_get_device_position (window, pointer, &x, &y, NULL); if (!pager->priv->dragging && pager->priv->drag_window != NULL && gtk_drag_check_threshold (widget, pager->priv->drag_start_x, pager->priv->drag_start_y, x, y)) { GtkTargetList *target_list; GdkDragContext *context; target_list = gtk_drag_dest_get_target_list (widget); context = gtk_drag_begin_with_coordinates (widget, target_list, GDK_ACTION_MOVE, 1, (GdkEvent *) event, -1, -1); pager->priv->dragging = TRUE; pager->priv->prelight_dnd = TRUE; _wnck_window_set_as_drag_icon (pager->priv->drag_window, context, GTK_WIDGET (pager)); } wnck_pager_check_prelight (pager, x, y, pager->priv->prelight_dnd); return TRUE; } static gboolean wnck_pager_leave_notify (GtkWidget *widget, GdkEventCrossing *event) { WnckPager *pager; pager = WNCK_PAGER (widget); wnck_pager_check_prelight (pager, -1, -1, FALSE); return FALSE; } static gboolean wnck_pager_button_release (GtkWidget *widget, GdkEventButton *event) { WnckWorkspace *space; WnckPager *pager; int i; int j; int viewport_x; int viewport_y; if (event->button != 1) return FALSE; pager = WNCK_PAGER (widget); if (!pager->priv->dragging) { i = workspace_at_point (pager, event->x, event->y, &viewport_x, &viewport_y); j = workspace_at_point (pager, pager->priv->drag_start_x, pager->priv->drag_start_y, NULL, NULL); if (i == j && i >= 0 && (space = wnck_screen_get_workspace (pager->priv->screen, i))) { int screen_width, screen_height; /* Don't switch the desktop if we're already there */ if (space != wnck_screen_get_active_workspace (pager->priv->screen)) wnck_workspace_activate (space, event->time); /* EWMH only lets us move the viewport for the active workspace, * but we just go ahead and hackily assume that the activate * just above takes effect prior to moving the viewport */ /* Transform the pointer location to viewport origin, assuming * that we want the nearest "regular" viewport containing the * pointer. */ screen_width = wnck_screen_get_width (pager->priv->screen); screen_height = wnck_screen_get_height (pager->priv->screen); viewport_x = (viewport_x / screen_width) * screen_width; viewport_y = (viewport_y / screen_height) * screen_height; if (wnck_workspace_get_viewport_x (space) != viewport_x || wnck_workspace_get_viewport_y (space) != viewport_y) wnck_screen_move_viewport (pager->priv->screen, viewport_x, viewport_y); } wnck_pager_clear_drag (pager); } return FALSE; } static gboolean wnck_pager_scroll_event (GtkWidget *widget, GdkEventScroll *event) { WnckPager *pager; WnckWorkspace *space; GdkScrollDirection absolute_direction; int index; int n_workspaces; int n_columns; int in_last_row; gboolean wrap_workspaces; gdouble smooth_x; gdouble smooth_y; pager = WNCK_PAGER (widget); if (event->type != GDK_SCROLL) return FALSE; if (event->direction == GDK_SCROLL_SMOOTH) return FALSE; if (pager->priv->scroll_mode == WNCK_PAGER_SCROLL_NONE) return FALSE; absolute_direction = event->direction; space = wnck_screen_get_active_workspace (pager->priv->screen); index = wnck_workspace_get_number (space); n_workspaces = wnck_screen_get_workspace_count (pager->priv->screen); n_columns = n_workspaces / pager->priv->n_rows; if (n_workspaces % pager->priv->n_rows != 0) n_columns++; in_last_row = n_workspaces % n_columns; wrap_workspaces = pager->priv->wrap_on_scroll; if (gtk_widget_get_direction (GTK_WIDGET (pager)) == GTK_TEXT_DIR_RTL) { switch (event->direction) { case GDK_SCROLL_DOWN: case GDK_SCROLL_UP: break; case GDK_SCROLL_RIGHT: absolute_direction = GDK_SCROLL_LEFT; break; case GDK_SCROLL_LEFT: absolute_direction = GDK_SCROLL_RIGHT; break; case GDK_SCROLL_SMOOTH: gdk_event_get_scroll_deltas ((GdkEvent*)event, &smooth_x, &smooth_y); if (smooth_x > 5) absolute_direction = GDK_SCROLL_RIGHT; else if (smooth_x < -5) absolute_direction = GDK_SCROLL_LEFT; break; default: break; } } if (pager->priv->scroll_mode == WNCK_PAGER_SCROLL_2D) { switch (absolute_direction) { case GDK_SCROLL_DOWN: if (index + n_columns < n_workspaces) { index += n_columns; } else if (wrap_workspaces && index == n_workspaces - 1) { index = 0; } else if ((index < n_workspaces - 1 && index + in_last_row != n_workspaces - 1) || (index == n_workspaces - 1 && in_last_row != 0)) { index = (index % n_columns) + 1; } break; case GDK_SCROLL_RIGHT: if (index < n_workspaces - 1) { index++; } else if (wrap_workspaces) { index = 0; } break; case GDK_SCROLL_UP: if (index - n_columns >= 0) { index -= n_columns; } else if (index > 0) { index = ((pager->priv->n_rows - 1) * n_columns) + (index % n_columns) - 1; } else if (wrap_workspaces) { index = n_workspaces - 1; } if (index >= n_workspaces) { index -= n_columns; } break; case GDK_SCROLL_LEFT: if (index > 0) { index--; } else if (wrap_workspaces) { index = n_workspaces - 1; } break; case GDK_SCROLL_SMOOTH: default: g_assert_not_reached (); break; } } else { switch (absolute_direction) { case GDK_SCROLL_UP: case GDK_SCROLL_LEFT: if (index > 0) { index--; } else if (wrap_workspaces) { index = n_workspaces - 1; } break; case GDK_SCROLL_DOWN: case GDK_SCROLL_RIGHT: if (index < n_workspaces - 1) { index++; } else if (wrap_workspaces) { index = 0; } break; case GDK_SCROLL_SMOOTH: default: g_assert_not_reached (); break; } } space = wnck_screen_get_workspace (pager->priv->screen, index); wnck_workspace_activate (space, event->time); return TRUE; } static gboolean wnck_pager_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip) { int i; WnckPager *pager; WnckScreen *screen; WnckWorkspace *space; char *name; pager = WNCK_PAGER (widget); screen = pager->priv->screen; i = workspace_at_point (pager, x, y, NULL, NULL); space = wnck_screen_get_workspace (screen, i); if (!space) return GTK_WIDGET_CLASS (wnck_pager_parent_class)->query_tooltip (widget, x, y, keyboard_tip, tooltip); if (wnck_screen_get_active_workspace (screen) == space) { WnckWindow *window; GdkRectangle workspace_rect; get_workspace_rect (pager, i, &workspace_rect); window = window_at_point (pager, space, &workspace_rect, x, y); if (window) name = g_strdup_printf (_("Click to start dragging \"%s\""), wnck_window_get_name (window)); else name = g_strdup_printf (_("Current workspace: \"%s\""), wnck_workspace_get_name (space)); } else { name = g_strdup_printf (_("Click to switch to \"%s\""), wnck_workspace_get_name (space)); } gtk_tooltip_set_text (tooltip, name); g_free (name); return TRUE; } /** * wnck_pager_new: * * Creates a new #WnckPager. The #WnckPager will show the #WnckWorkspace of the * #WnckScreen it is on. * * Return value: a newly created #WnckPager. */ GtkWidget* wnck_pager_new (void) { WnckPager *pager; pager = g_object_new (WNCK_TYPE_PAGER, "handle", _wnck_get_handle (), NULL); return GTK_WIDGET (pager); } /** * wnck_pager_new_with_handle: * @handle: a #WnckHandle * * Creates a new #WnckPager. The #WnckPager will show the #WnckWorkspace of the * #WnckScreen it is on. * * Returns: a newly created #WnckPager. */ GtkWidget * wnck_pager_new_with_handle (WnckHandle *handle) { WnckPager *self; self = g_object_new (WNCK_TYPE_PAGER, "handle", handle, NULL); return GTK_WIDGET (self); } static gboolean wnck_pager_set_layout_hint (WnckPager *pager) { int layout_rows; int layout_cols; /* if we're not realized, we don't know about our screen yet */ if (pager->priv->screen == NULL) _wnck_pager_set_screen (pager); /* can still happen if the pager was not added to a widget hierarchy */ if (pager->priv->screen == NULL) return FALSE; /* The visual representation of the pager doesn't * correspond to the layout of the workspaces * here. i.e. the user will not pay any attention * to the n_rows setting on this pager. */ if (!pager->priv->show_all_workspaces) return FALSE; if (pager->priv->orientation == GTK_ORIENTATION_HORIZONTAL) { layout_rows = pager->priv->n_rows; layout_cols = 0; } else { layout_rows = 0; layout_cols = pager->priv->n_rows; } pager->priv->layout_manager_token = wnck_screen_try_set_workspace_layout (pager->priv->screen, pager->priv->layout_manager_token, layout_rows, layout_cols); return (pager->priv->layout_manager_token != WNCK_NO_MANAGER_TOKEN); } /** * wnck_pager_set_orientation: * @pager: a #WnckPager. * @orientation: orientation to use for the layout of #WnckWorkspace on the * #WnckScreen @pager is watching. * * Tries to change the orientation of the layout of #WnckWorkspace on the * #WnckScreen @pager is watching. Since no more than one application should * set this property of a #WnckScreen at a time, setting the layout is not * guaranteed to work. * * If @orientation is %GTK_ORIENTATION_HORIZONTAL, the #WnckWorkspace will be * laid out in rows, with the first #WnckWorkspace in the top left corner. * * If @orientation is %GTK_ORIENTATION_VERTICAL, the #WnckWorkspace will be * laid out in columns, with the first #WnckWorkspace in the top left corner. * * For example, if the layout contains one row, but the orientation of the * layout is vertical, the #WnckPager will display a column of #WnckWorkspace. * * Note that setting the orientation will have an effect on the geometry * management: if @orientation is %GTK_ORIENTATION_HORIZONTAL, * %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT will be used as request mode; if * @orientation is %GTK_ORIENTATION_VERTICAL, GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH * will be used instead. * * If @pager has not been added to a widget hierarchy, the call will fail * because @pager can't know the screen on which to modify the orientation. * * Return value: %TRUE if the layout of #WnckWorkspace has been successfully * changed or did not need to be changed, %FALSE otherwise. */ gboolean wnck_pager_set_orientation (WnckPager *pager, GtkOrientation orientation) { GtkOrientation old_orientation; gboolean old_orientation_is_valid; g_return_val_if_fail (WNCK_IS_PAGER (pager), FALSE); if (pager->priv->orientation == orientation) return TRUE; old_orientation = pager->priv->orientation; old_orientation_is_valid = pager->priv->screen != NULL; pager->priv->orientation = orientation; if (wnck_pager_set_layout_hint (pager)) { gtk_widget_queue_resize (GTK_WIDGET (pager)); return TRUE; } else { if (old_orientation_is_valid) pager->priv->orientation = old_orientation; return FALSE; } } /** * wnck_pager_set_n_rows: * @pager: a #WnckPager. * @n_rows: the number of rows to use for the layout of #WnckWorkspace on the * #WnckScreen @pager is watching. * * Tries to change the number of rows in the layout of #WnckWorkspace on the * #WnckScreen @pager is watching. Since no more than one application should * set this property of a #WnckScreen at a time, setting the layout is not * guaranteed to work. * * If @pager has not been added to a widget hierarchy, the call will fail * because @pager can't know the screen on which to modify the layout. * * Return value: %TRUE if the layout of #WnckWorkspace has been successfully * changed or did not need to be changed, %FALSE otherwise. */ gboolean wnck_pager_set_n_rows (WnckPager *pager, int n_rows) { int old_n_rows; gboolean old_n_rows_is_valid; g_return_val_if_fail (WNCK_IS_PAGER (pager), FALSE); g_return_val_if_fail (n_rows > 0, FALSE); if (pager->priv->n_rows == n_rows) return TRUE; old_n_rows = pager->priv->n_rows; old_n_rows_is_valid = pager->priv->screen != NULL; pager->priv->n_rows = n_rows; if (wnck_pager_set_layout_hint (pager)) { gtk_widget_queue_resize (GTK_WIDGET (pager)); return TRUE; } else { if (old_n_rows_is_valid) pager->priv->n_rows = old_n_rows; return FALSE; } } /** * wnck_pager_set_display_mode: * @pager: a #WnckPager. * @mode: a display mode. * * Sets the display mode for @pager to @mode. */ void wnck_pager_set_display_mode (WnckPager *pager, WnckPagerDisplayMode mode) { g_return_if_fail (WNCK_IS_PAGER (pager)); if (pager->priv->display_mode == mode) return; g_object_set (pager, "has-tooltip", mode != WNCK_PAGER_DISPLAY_NAME, NULL); pager->priv->display_mode = mode; gtk_widget_queue_resize (GTK_WIDGET (pager)); } /** * wnck_pager_set_scroll_mode: * @pager: a #WnckPager. * @scroll_mode: a scroll mode. * * Sets @pager to react to input device scrolling in one of the * available scroll modes. * * Since: 3.36 */ void wnck_pager_set_scroll_mode (WnckPager *pager, WnckPagerScrollMode scroll_mode) { g_return_if_fail (WNCK_IS_PAGER (pager)); if (pager->priv->scroll_mode == scroll_mode) return; pager->priv->scroll_mode = scroll_mode; } /** * wnck_pager_set_show_all: * @pager: a #WnckPager. * @show_all_workspaces: whether to display all #WnckWorkspace in @pager. * * Sets @pager to display all #WnckWorkspace or not, according to * @show_all_workspaces. */ void wnck_pager_set_show_all (WnckPager *pager, gboolean show_all_workspaces) { g_return_if_fail (WNCK_IS_PAGER (pager)); show_all_workspaces = (show_all_workspaces != 0); if (pager->priv->show_all_workspaces == show_all_workspaces) return; pager->priv->show_all_workspaces = show_all_workspaces; gtk_widget_queue_resize (GTK_WIDGET (pager)); } /** * wnck_pager_set_shadow_type: * @pager: a #WnckPager. * @shadow_type: a shadow type. * * Sets the shadow type for @pager to @shadow_type. The main use of this * function is proper integration of #WnckPager in panels with non-system * backgrounds. * * Since: 2.2 */ void wnck_pager_set_shadow_type (WnckPager * pager, GtkShadowType shadow_type) { g_return_if_fail (WNCK_IS_PAGER (pager)); if (pager->priv->shadow_type == shadow_type) return; pager->priv->shadow_type = shadow_type; gtk_widget_queue_resize (GTK_WIDGET (pager)); } /** * wnck_pager_set_wrap_on_scroll: * @pager: a #WnckPager. * @wrap_on_scroll: a boolean. * * Sets the wrapping behavior of the @pager. Setting it to %TRUE will * wrap arround to the start when scrolling over the end and vice * versa. By default it is set to %FALSE. * * Since: 3.24.0 */ void wnck_pager_set_wrap_on_scroll (WnckPager *pager, gboolean wrap_on_scroll) { g_return_if_fail (WNCK_IS_PAGER (pager)); pager->priv->wrap_on_scroll = wrap_on_scroll; } /** * wnck_pager_get_wrap_on_scroll: * @pager: a #WnckPager. * * Return value: %TRUE if the @pager wraps workspaces on a scroll event that * hits a border, %FALSE otherwise. * * Since: 3.24.0 */ gboolean wnck_pager_get_wrap_on_scroll (WnckPager *pager) { g_return_val_if_fail (WNCK_IS_PAGER (pager), FALSE); return pager->priv->wrap_on_scroll; } static void active_window_changed_callback (WnckScreen *screen, WnckWindow *previous_window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); gtk_widget_queue_draw (GTK_WIDGET (pager)); } static void active_workspace_changed_callback (WnckScreen *screen, WnckWorkspace *previous_workspace, gpointer data) { WnckPager *pager = WNCK_PAGER (data); gtk_widget_queue_draw (GTK_WIDGET (pager)); } static void window_stacking_changed_callback (WnckScreen *screen, gpointer data) { WnckPager *pager = WNCK_PAGER (data); gtk_widget_queue_draw (GTK_WIDGET (pager)); } static void window_opened_callback (WnckScreen *screen, WnckWindow *window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); wnck_pager_connect_window (pager, window); wnck_pager_queue_draw_window (pager, window); } static void window_closed_callback (WnckScreen *screen, WnckWindow *window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); if (pager->priv->drag_window == window) wnck_pager_clear_drag (pager); wnck_pager_queue_draw_window (pager, window); } static void workspace_created_callback (WnckScreen *screen, WnckWorkspace *space, gpointer data) { WnckPager *pager = WNCK_PAGER (data); g_signal_connect (space, "name_changed", G_CALLBACK (workspace_name_changed_callback), pager); gtk_widget_queue_resize (GTK_WIDGET (pager)); } static void workspace_destroyed_callback (WnckScreen *screen, WnckWorkspace *space, gpointer data) { WnckPager *pager = WNCK_PAGER (data); g_signal_handlers_disconnect_by_func (space, G_CALLBACK (workspace_name_changed_callback), pager); gtk_widget_queue_resize (GTK_WIDGET (pager)); } static void application_opened_callback (WnckScreen *screen, WnckApplication *app, gpointer data) { /* WnckPager *pager = WNCK_PAGER (data); */ } static void application_closed_callback (WnckScreen *screen, WnckApplication *app, gpointer data) { /* WnckPager *pager = WNCK_PAGER (data); */ } static void window_name_changed_callback (WnckWindow *window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); wnck_pager_queue_draw_window (pager, window); } static void window_state_changed_callback (WnckWindow *window, WnckWindowState changed, WnckWindowState new, gpointer data) { WnckPager *pager = WNCK_PAGER (data); /* if the changed state changes the visibility in the pager, we need to * redraw the whole workspace. wnck_pager_queue_draw_window() might not be * enough */ if (!wnck_pager_window_state_is_relevant (changed)) wnck_pager_queue_draw_workspace (pager, wnck_pager_window_get_workspace (window, FALSE)); else wnck_pager_queue_draw_window (pager, window); } static void window_workspace_changed_callback (WnckWindow *window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); gtk_widget_queue_draw (GTK_WIDGET (pager)); } static void window_icon_changed_callback (WnckWindow *window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); wnck_pager_queue_draw_window (pager, window); } static void window_geometry_changed_callback (WnckWindow *window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); wnck_pager_queue_draw_window (pager, window); } static void background_changed_callback (WnckWindow *window, gpointer data) { WnckPager *pager = WNCK_PAGER (data); if (pager->priv->bg_cache) { g_object_unref (G_OBJECT (pager->priv->bg_cache)); pager->priv->bg_cache = NULL; } gtk_widget_queue_draw (GTK_WIDGET (pager)); } static void workspace_name_changed_callback (WnckWorkspace *space, gpointer data) { gtk_widget_queue_resize (GTK_WIDGET (data)); } static void viewports_changed_callback (WnckWorkspace *space, gpointer data) { gtk_widget_queue_resize (GTK_WIDGET (data)); } static void wnck_pager_connect_screen (WnckPager *pager) { int i; guint *c; GList *tmp; WnckScreen *screen; g_return_if_fail (pager->priv->screen != NULL); screen = pager->priv->screen; for (tmp = wnck_screen_get_windows (screen); tmp; tmp = tmp->next) { wnck_pager_connect_window (pager, WNCK_WINDOW (tmp->data)); } i = 0; c = pager->priv->screen_connections; c[i] = g_signal_connect (G_OBJECT (screen), "active_window_changed", G_CALLBACK (active_window_changed_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "active_workspace_changed", G_CALLBACK (active_workspace_changed_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "window_stacking_changed", G_CALLBACK (window_stacking_changed_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "window_opened", G_CALLBACK (window_opened_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "window_closed", G_CALLBACK (window_closed_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "workspace_created", G_CALLBACK (workspace_created_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "workspace_destroyed", G_CALLBACK (workspace_destroyed_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "application_opened", G_CALLBACK (application_opened_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "application_closed", G_CALLBACK (application_closed_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "background_changed", G_CALLBACK (background_changed_callback), pager); ++i; c[i] = g_signal_connect (G_OBJECT (screen), "viewports_changed", G_CALLBACK (viewports_changed_callback), pager); ++i; g_assert (i == N_SCREEN_CONNECTIONS); /* connect to name_changed on each workspace */ for (i = 0; i < wnck_screen_get_workspace_count (pager->priv->screen); i++) { WnckWorkspace *space; space = wnck_screen_get_workspace (pager->priv->screen, i); g_signal_connect (space, "name_changed", G_CALLBACK (workspace_name_changed_callback), pager); } } static void wnck_pager_connect_window (WnckPager *pager, WnckWindow *window) { g_signal_connect (G_OBJECT (window), "name_changed", G_CALLBACK (window_name_changed_callback), pager); g_signal_connect (G_OBJECT (window), "state_changed", G_CALLBACK (window_state_changed_callback), pager); g_signal_connect (G_OBJECT (window), "workspace_changed", G_CALLBACK (window_workspace_changed_callback), pager); g_signal_connect (G_OBJECT (window), "icon_changed", G_CALLBACK (window_icon_changed_callback), pager); g_signal_connect (G_OBJECT (window), "geometry_changed", G_CALLBACK (window_geometry_changed_callback), pager); } static void wnck_pager_disconnect_screen (WnckPager *pager) { int i; GList *tmp; if (pager->priv->screen == NULL) return; i = 0; while (i < N_SCREEN_CONNECTIONS) { if (pager->priv->screen_connections[i] != 0) g_signal_handler_disconnect (G_OBJECT (pager->priv->screen), pager->priv->screen_connections[i]); pager->priv->screen_connections[i] = 0; ++i; } for (i = 0; i < wnck_screen_get_workspace_count (pager->priv->screen); i++) { WnckWorkspace *space; space = wnck_screen_get_workspace (pager->priv->screen, i); g_signal_handlers_disconnect_by_func (space, G_CALLBACK (workspace_name_changed_callback), pager); } for (tmp = wnck_screen_get_windows (pager->priv->screen); tmp; tmp = tmp->next) { wnck_pager_disconnect_window (pager, WNCK_WINDOW (tmp->data)); } } static void wnck_pager_disconnect_window (WnckPager *pager, WnckWindow *window) { g_signal_handlers_disconnect_by_func (G_OBJECT (window), G_CALLBACK (window_name_changed_callback), pager); g_signal_handlers_disconnect_by_func (G_OBJECT (window), G_CALLBACK (window_state_changed_callback), pager); g_signal_handlers_disconnect_by_func (G_OBJECT (window), G_CALLBACK (window_workspace_changed_callback), pager); g_signal_handlers_disconnect_by_func (G_OBJECT (window), G_CALLBACK (window_icon_changed_callback), pager); g_signal_handlers_disconnect_by_func (G_OBJECT (window), G_CALLBACK (window_geometry_changed_callback), pager); } static void wnck_pager_clear_drag (WnckPager *pager) { if (pager->priv->dragging) wnck_pager_queue_draw_window (pager, pager->priv->drag_window); pager->priv->dragging = FALSE; pager->priv->drag_window = NULL; pager->priv->drag_start_x = -1; pager->priv->drag_start_y = -1; } static GdkPixbuf* wnck_pager_get_background (WnckPager *pager, int width, int height) { Pixmap p; GdkPixbuf *pix = NULL; /* We have to be careful not to keep alternating between * width/height values, otherwise this would get really slow. */ if (pager->priv->bg_cache && gdk_pixbuf_get_width (pager->priv->bg_cache) == width && gdk_pixbuf_get_height (pager->priv->bg_cache) == height) return pager->priv->bg_cache; if (pager->priv->bg_cache) { g_object_unref (G_OBJECT (pager->priv->bg_cache)); pager->priv->bg_cache = NULL; } if (pager->priv->screen == NULL) return NULL; /* FIXME this just globally disables the thumbnailing feature */ return NULL; #define MIN_BG_SIZE 10 if (width < MIN_BG_SIZE || height < MIN_BG_SIZE) return NULL; p = wnck_screen_get_background_pixmap (pager->priv->screen); if (p != None) { Screen *xscreen; xscreen = WNCK_SCREEN_XSCREEN (pager->priv->screen); pix = _wnck_gdk_pixbuf_get_from_pixmap (xscreen, p); } if (pix) { pager->priv->bg_cache = gdk_pixbuf_scale_simple (pix, width, height, GDK_INTERP_BILINEAR); g_object_unref (G_OBJECT (pix)); } return pager->priv->bg_cache; } /* *This will return aobj_pager whose parent is wnck's atk object -Gail Container */ static AtkObject * wnck_pager_get_accessible (GtkWidget *widget) { static gboolean first_time = TRUE; if (first_time) { AtkObjectFactory *factory; AtkRegistry *registry; GType derived_type; GType derived_atk_type; /* * Figure out whether accessibility is enabled by looking at the * type of the accessible object which would be created for * the parent type WnckPager. */ derived_type = g_type_parent (WNCK_TYPE_PAGER); registry = atk_get_default_registry (); factory = atk_registry_get_factory (registry, derived_type); derived_atk_type = atk_object_factory_get_accessible_type (factory); if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE)) { /* * Specify what factory to use to create accessible * objects */ atk_registry_set_factory_type (registry, WNCK_TYPE_PAGER, WNCK_TYPE_PAGER_ACCESSIBLE_FACTORY); atk_registry_set_factory_type (registry, WNCK_TYPE_WORKSPACE, WNCK_TYPE_WORKSPACE_ACCESSIBLE_FACTORY); } first_time = FALSE; } return GTK_WIDGET_CLASS (wnck_pager_parent_class)->get_accessible (widget); } int _wnck_pager_get_n_workspaces (WnckPager *pager) { return wnck_screen_get_workspace_count (pager->priv->screen); } const char* _wnck_pager_get_workspace_name (WnckPager *pager, int i) { WnckWorkspace *space; space = wnck_screen_get_workspace (pager->priv->screen, i); if (space) return wnck_workspace_get_name (space); else return NULL; } WnckWorkspace* _wnck_pager_get_active_workspace (WnckPager *pager) { return wnck_screen_get_active_workspace (pager->priv->screen); } WnckWorkspace* _wnck_pager_get_workspace (WnckPager *pager, int i) { return wnck_screen_get_workspace (pager->priv->screen, i); } void _wnck_pager_activate_workspace (WnckWorkspace *wspace, guint32 timestamp) { wnck_workspace_activate (wspace, timestamp); } void _wnck_pager_get_workspace_rect (WnckPager *pager, int i, GdkRectangle *rect) { get_workspace_rect (pager, i, rect); }