1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 et: */
3 /* pager object */
4 
5 /*
6  * Copyright (C) 2001 Havoc Pennington
7  * Copyright (C) 2003 Kim Woelders
8  * Copyright (C) 2003 Red Hat, Inc.
9  * Copyright (C) 2003, 2005-2007 Vincent Untz
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License along with this library; if not, write to the
23  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26 
27 #undef WNCK_DISABLE_DEPRECATED
28 
29 #include <config.h>
30 
31 #include <math.h>
32 #include <glib/gi18n-lib.h>
33 
34 #include "pager.h"
35 #include "workspace.h"
36 #include "window.h"
37 #include "xutils.h"
38 #include "pager-accessible-factory.h"
39 #include "workspace-accessible-factory.h"
40 #include "private.h"
41 
42 /**
43  * SECTION:pager
44  * @short_description: a pager widget, showing the content of workspaces.
45  * @see_also: #WnckScreen
46  * @stability: Unstable
47  *
48  * A #WnckPager shows a miniature view of the workspaces, representing managed
49  * windows by small rectangles, and allows the user to initiate various window
50  * manager actions by manipulating these representations. The #WnckPager offers
51  * ways to move windows between workspaces and to change the current workspace.
52  *
53  * Alternatively, a #WnckPager can be configured to only show the names of the
54  * workspace instead of their contents.
55  *
56  * The #WnckPager is also responsible for setting the layout of the workspaces.
57  * Since only one application can be responsible for setting the layout on a
58  * screen, the #WnckPager automatically tries to obtain the manager selection
59  * for the screen and only sets the layout if it owns the manager selection.
60  * See wnck_pager_set_orientation() and wnck_pager_set_n_rows() to change the
61  * layout.
62  */
63 
64 #define N_SCREEN_CONNECTIONS 11
65 
66 struct _WnckPagerPrivate
67 {
68   WnckScreen *screen;
69 
70   int n_rows; /* really columns for vertical orientation */
71   WnckPagerDisplayMode display_mode;
72   gboolean show_all_workspaces;
73   GtkShadowType shadow_type;
74 
75   GtkOrientation orientation;
76   int workspace_size;
77   guint screen_connections[N_SCREEN_CONNECTIONS];
78   int prelight; /* workspace mouse is hovering over */
79   gboolean prelight_dnd; /* is dnd happening? */
80 
81   guint dragging :1;
82   int drag_start_x;
83   int drag_start_y;
84   WnckWindow *drag_window;
85 
86   GdkPixbuf *bg_cache;
87 
88   int layout_manager_token;
89 
90   guint dnd_activate; /* GSource that triggers switching to this workspace during dnd */
91   guint dnd_time; /* time of last event during dnd (for delayed workspace activation) */
92 };
93 
94 G_DEFINE_TYPE (WnckPager, wnck_pager, GTK_TYPE_WIDGET);
95 #define WNCK_PAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WNCK_TYPE_PAGER, WnckPagerPrivate))
96 
97 enum
98 {
99   dummy, /* remove this when you add more signals */
100   LAST_SIGNAL
101 };
102 
103 
104 #define POINT_IN_RECT(xcoord, ycoord, rect) \
105  ((xcoord) >= (rect).x &&                   \
106   (xcoord) <  ((rect).x + (rect).width) &&  \
107   (ycoord) >= (rect).y &&                   \
108   (ycoord) <  ((rect).y + (rect).height))
109 
110 static void wnck_pager_init        (WnckPager      *pager);
111 static void wnck_pager_class_init  (WnckPagerClass *klass);
112 static void wnck_pager_finalize    (GObject        *object);
113 
114 static void     wnck_pager_realize       (GtkWidget        *widget);
115 static void     wnck_pager_unrealize     (GtkWidget        *widget);
116 static void     wnck_pager_size_request  (GtkWidget        *widget,
117                                           GtkRequisition   *requisition);
118 static void     wnck_pager_size_allocate (GtkWidget        *widget,
119                                           GtkAllocation    *allocation);
120 static gboolean wnck_pager_expose_event  (GtkWidget        *widget,
121                                           GdkEventExpose   *event);
122 static gboolean wnck_pager_button_press  (GtkWidget        *widget,
123                                           GdkEventButton   *event);
124 static gboolean wnck_pager_drag_motion   (GtkWidget        *widget,
125                                           GdkDragContext   *context,
126                                           gint              x,
127                                           gint              y,
128                                           guint             time);
129 static void wnck_pager_drag_motion_leave (GtkWidget        *widget,
130                                           GdkDragContext   *context,
131                                           guint             time);
132 static gboolean wnck_pager_drag_drop	 (GtkWidget        *widget,
133 					  GdkDragContext   *context,
134 					  gint              x,
135 					  gint              y,
136 					  guint             time);
137 static void wnck_pager_drag_data_received (GtkWidget          *widget,
138 			  	           GdkDragContext     *context,
139 				           gint                x,
140 				           gint                y,
141 				           GtkSelectionData   *selection_data,
142 				           guint               info,
143 				           guint               time_);
144 static void wnck_pager_drag_data_get      (GtkWidget        *widget,
145 		                           GdkDragContext   *context,
146 		                           GtkSelectionData *selection_data,
147 		                           guint             info,
148 		                           guint             time);
149 static void wnck_pager_drag_end		  (GtkWidget        *widget,
150 					   GdkDragContext   *context);
151 static gboolean wnck_pager_motion        (GtkWidget        *widget,
152                                           GdkEventMotion   *event);
153 static gboolean wnck_pager_leave_notify	 (GtkWidget          *widget,
154 					  GdkEventCrossing   *event);
155 static gboolean wnck_pager_button_release (GtkWidget        *widget,
156                                            GdkEventButton   *event);
157 static gboolean wnck_pager_focus         (GtkWidget        *widget,
158                                           GtkDirectionType  direction);
159 static gboolean wnck_pager_query_tooltip (GtkWidget  *widget,
160                                           gint        x,
161                                           gint        y,
162                                           gboolean    keyboard_tip,
163                                           GtkTooltip *tooltip);
164 static void workspace_name_changed_callback (WnckWorkspace *workspace,
165                                              gpointer       data);
166 
167 static gboolean wnck_pager_window_state_is_relevant (int state);
168 static gint wnck_pager_window_get_workspace   (WnckWindow  *window,
169                                                gboolean     is_state_relevant);
170 static void wnck_pager_queue_draw_workspace   (WnckPager   *pager,
171 					       gint	    i);
172 static void wnck_pager_queue_draw_window (WnckPager	   *pager,
173 					  WnckWindow	   *window);
174 
175 static void wnck_pager_connect_screen    (WnckPager  *pager);
176 static void wnck_pager_connect_window    (WnckPager  *pager,
177                                           WnckWindow *window);
178 static void wnck_pager_disconnect_screen (WnckPager  *pager);
179 static void wnck_pager_disconnect_window (WnckPager  *pager,
180                                           WnckWindow *window);
181 
182 static gboolean wnck_pager_set_layout_hint (WnckPager  *pager);
183 
184 static void wnck_pager_clear_drag (WnckPager *pager);
185 static void wnck_pager_check_prelight (WnckPager *pager,
186 				       gint	  x,
187 				       gint	  y,
188 				       gboolean	  dnd);
189 
190 static GdkPixbuf* wnck_pager_get_background (WnckPager *pager,
191                                              int        width,
192                                              int        height);
193 
194 static AtkObject* wnck_pager_get_accessible (GtkWidget *widget);
195 
196 
197 static void
wnck_pager_init(WnckPager * pager)198 wnck_pager_init (WnckPager *pager)
199 {
200   int i;
201   static const GtkTargetEntry targets[] = {
202     { "application/x-wnck-window-id", 0, 0}
203   };
204 
205   pager->priv = WNCK_PAGER_GET_PRIVATE (pager);
206 
207   pager->priv->n_rows = 1;
208   pager->priv->display_mode = WNCK_PAGER_DISPLAY_CONTENT;
209   pager->priv->show_all_workspaces = TRUE;
210   pager->priv->shadow_type = GTK_SHADOW_NONE;
211 
212   pager->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
213   pager->priv->workspace_size = 48;
214 
215   for (i = 0; i < N_SCREEN_CONNECTIONS; i++)
216     pager->priv->screen_connections[i] = 0;
217 
218   pager->priv->prelight = -1;
219   pager->priv->prelight_dnd = FALSE;
220 
221   pager->priv->dragging = FALSE;
222   pager->priv->drag_start_x = 0;
223   pager->priv->drag_start_y = 0;
224   pager->priv->drag_window = NULL;
225 
226   pager->priv->bg_cache = NULL;
227 
228   pager->priv->layout_manager_token = WNCK_NO_MANAGER_TOKEN;
229 
230   pager->priv->dnd_activate = 0;
231   pager->priv->dnd_time = 0;
232 
233   g_object_set (pager, "has-tooltip", TRUE, NULL);
234 
235   gtk_drag_dest_set (GTK_WIDGET (pager), 0, targets, G_N_ELEMENTS (targets), GDK_ACTION_MOVE);
236   gtk_widget_set_can_focus (GTK_WIDGET (pager), TRUE);
237 }
238 
239 static void
wnck_pager_class_init(WnckPagerClass * klass)240 wnck_pager_class_init (WnckPagerClass *klass)
241 {
242   GObjectClass *object_class = G_OBJECT_CLASS (klass);
243   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
244 
245   g_type_class_add_private (klass, sizeof (WnckPagerPrivate));
246 
247   object_class->finalize = wnck_pager_finalize;
248 
249   widget_class->realize = wnck_pager_realize;
250   widget_class->unrealize = wnck_pager_unrealize;
251   widget_class->size_request = wnck_pager_size_request;
252   widget_class->size_allocate = wnck_pager_size_allocate;
253   widget_class->expose_event = wnck_pager_expose_event;
254   widget_class->button_press_event = wnck_pager_button_press;
255   widget_class->button_release_event = wnck_pager_button_release;
256   widget_class->motion_notify_event = wnck_pager_motion;
257   widget_class->leave_notify_event = wnck_pager_leave_notify;
258   widget_class->focus = wnck_pager_focus;
259   widget_class->get_accessible = wnck_pager_get_accessible;
260   widget_class->drag_leave = wnck_pager_drag_motion_leave;
261   widget_class->drag_motion = wnck_pager_drag_motion;
262   widget_class->drag_drop = wnck_pager_drag_drop;
263   widget_class->drag_data_received = wnck_pager_drag_data_received;
264   widget_class->drag_data_get = wnck_pager_drag_data_get;
265   widget_class->drag_end = wnck_pager_drag_end;
266   widget_class->query_tooltip = wnck_pager_query_tooltip;
267 }
268 
269 static void
wnck_pager_finalize(GObject * object)270 wnck_pager_finalize (GObject *object)
271 {
272   WnckPager *pager;
273 
274   pager = WNCK_PAGER (object);
275 
276   if (pager->priv->bg_cache)
277     {
278       g_object_unref (G_OBJECT (pager->priv->bg_cache));
279       pager->priv->bg_cache = NULL;
280     }
281 
282   if (pager->priv->dnd_activate != 0)
283     {
284       g_source_remove (pager->priv->dnd_activate);
285       pager->priv->dnd_activate = 0;
286     }
287 
288   G_OBJECT_CLASS (wnck_pager_parent_class)->finalize (object);
289 }
290 
291 static void
_wnck_pager_set_screen(WnckPager * pager)292 _wnck_pager_set_screen (WnckPager *pager)
293 {
294   GdkScreen *gdkscreen;
295 
296   if (!gtk_widget_has_screen (GTK_WIDGET (pager)))
297     return;
298 
299   gdkscreen = gtk_widget_get_screen (GTK_WIDGET (pager));
300   pager->priv->screen = wnck_screen_get (gdk_screen_get_number (gdkscreen));
301 
302   if (!wnck_pager_set_layout_hint (pager))
303     {
304       _WnckLayoutOrientation orientation;
305 
306       /* we couldn't set the layout on the screen. This means someone else owns
307        * it. Let's at least show the correct layout. */
308       _wnck_screen_get_workspace_layout (pager->priv->screen,
309                                          &orientation,
310                                          &pager->priv->n_rows,
311                                          NULL, NULL);
312 
313       /* test in this order to default to horizontal in case there was in issue
314        * when fetching the layout */
315       if (orientation == WNCK_LAYOUT_ORIENTATION_VERTICAL)
316         pager->priv->orientation = GTK_ORIENTATION_VERTICAL;
317       else
318         pager->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
319 
320       gtk_widget_queue_resize (GTK_WIDGET (pager));
321     }
322 
323   wnck_pager_connect_screen (pager);
324 }
325 
326 static void
wnck_pager_realize(GtkWidget * widget)327 wnck_pager_realize (GtkWidget *widget)
328 {
329 
330   GdkWindowAttr attributes;
331   gint attributes_mask;
332   WnckPager *pager;
333   GtkAllocation allocation;
334   GdkWindow *window;
335   GtkStyle *style;
336   GtkStyle *new_style;
337 
338   pager = WNCK_PAGER (widget);
339 
340   /* do not call the parent class realize since we're doing things a bit
341    * differently here */
342   gtk_widget_set_realized (widget, TRUE);
343 
344   gtk_widget_get_allocation (widget, &allocation);
345 
346   attributes.window_type = GDK_WINDOW_CHILD;
347   attributes.x = allocation.x;
348   attributes.y = allocation.y;
349   attributes.width = allocation.width;
350   attributes.height = allocation.height;
351   attributes.wclass = GDK_INPUT_OUTPUT;
352   attributes.visual = gtk_widget_get_visual (widget);
353   attributes.colormap = gtk_widget_get_colormap (widget);
354   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK |
355 	  		  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
356 			  GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK |
357 			  GDK_POINTER_MOTION_HINT_MASK;
358 
359   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
360 
361   window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
362   gtk_widget_set_window (widget, window);
363   gdk_window_set_user_data (window, widget);
364 
365   style = gtk_widget_get_style (widget);
366 
367   new_style = gtk_style_attach (style, window);
368   if (new_style != style)
369     {
370       gtk_widget_set_style (widget, style);
371       style = new_style;
372     }
373 
374   gtk_style_set_background (style, window, GTK_STATE_NORMAL);
375 
376   /* connect to the screen of this pager. In theory, this will already have
377    * been done in wnck_pager_size_request() */
378   if (pager->priv->screen == NULL)
379     _wnck_pager_set_screen (pager);
380   g_assert (pager->priv->screen != NULL);
381 }
382 
383 static void
wnck_pager_unrealize(GtkWidget * widget)384 wnck_pager_unrealize (GtkWidget *widget)
385 {
386   WnckPager *pager;
387 
388   pager = WNCK_PAGER (widget);
389 
390   wnck_pager_clear_drag (pager);
391   pager->priv->prelight = -1;
392   pager->priv->prelight_dnd = FALSE;
393 
394   wnck_screen_release_workspace_layout (pager->priv->screen,
395                                         pager->priv->layout_manager_token);
396   pager->priv->layout_manager_token = WNCK_NO_MANAGER_TOKEN;
397 
398   wnck_pager_disconnect_screen (pager);
399   pager->priv->screen = NULL;
400 
401   GTK_WIDGET_CLASS (wnck_pager_parent_class)->unrealize (widget);
402 }
403 
404 static void
wnck_pager_size_request(GtkWidget * widget,GtkRequisition * requisition)405 wnck_pager_size_request  (GtkWidget      *widget,
406                           GtkRequisition *requisition)
407 {
408   WnckPager *pager;
409   int n_spaces;
410   int spaces_per_row;
411   double screen_aspect;
412   int other_dimension_size;
413   int size;
414   int n_rows;
415   int focus_width;
416   WnckWorkspace *space;
417 
418   pager = WNCK_PAGER (widget);
419 
420   /* if we're not realized, we don't know about our screen yet */
421   if (pager->priv->screen == NULL)
422     _wnck_pager_set_screen (pager);
423   g_assert (pager->priv->screen != NULL);
424 
425   n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
426 
427   g_assert (pager->priv->n_rows > 0);
428   spaces_per_row = (n_spaces + pager->priv->n_rows - 1) / pager->priv->n_rows;
429   space = wnck_screen_get_workspace (pager->priv->screen, 0);
430 
431   if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL)
432     {
433       if (space) {
434         screen_aspect =
435               (double) wnck_workspace_get_height (space) /
436               (double) wnck_workspace_get_width (space);
437       } else {
438         screen_aspect =
439               (double) wnck_screen_get_height (pager->priv->screen) /
440               (double) wnck_screen_get_width (pager->priv->screen);
441       }
442 
443       /* TODO: Handle WNCK_PAGER_DISPLAY_NAME for this case */
444 
445       if (pager->priv->show_all_workspaces)
446 	{
447 	  size = pager->priv->workspace_size;
448 	  n_rows = pager->priv->n_rows;
449 	}
450       else
451 	{
452 	  size = pager->priv->workspace_size;
453 	  n_rows = 1;
454 	  spaces_per_row = 1;
455 	}
456 
457       other_dimension_size = screen_aspect * size;
458 
459       requisition->width = size * n_rows + (n_rows - 1);
460       requisition->height = other_dimension_size * spaces_per_row  + (spaces_per_row - 1);
461     }
462   else
463     {
464       if (space) {
465         screen_aspect =
466               (double) wnck_workspace_get_width (space) /
467               (double) wnck_workspace_get_height (space);
468       } else {
469         screen_aspect =
470               (double) wnck_screen_get_width (pager->priv->screen) /
471               (double) wnck_screen_get_height (pager->priv->screen);
472       }
473 
474       if (pager->priv->show_all_workspaces)
475 	{
476 	  size = pager->priv->workspace_size;
477 	  n_rows = pager->priv->n_rows;
478 	}
479       else
480 	{
481 	  size = pager->priv->workspace_size;
482 	  n_rows = 1;
483 	  spaces_per_row = 1;
484 	}
485 
486       if (pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT)
487 	{
488 	  other_dimension_size = screen_aspect * size;
489 	}
490       else
491 	{
492 	  int n_spaces, i, w;
493 	  WnckScreen *screen;
494 	  PangoLayout *layout;
495 
496 	  n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
497 	  layout = gtk_widget_create_pango_layout  (widget, NULL);
498 	  screen = pager->priv->screen;
499 	  other_dimension_size = 1;
500 
501 	  for (i = 0; i < n_spaces; i++)
502 	    {
503 	      pango_layout_set_text (layout,
504 				     wnck_workspace_get_name (wnck_screen_get_workspace (screen, i)),
505 				     -1);
506 	      pango_layout_get_pixel_size (layout, &w, NULL);
507 	      other_dimension_size = MAX (other_dimension_size, w);
508 	    }
509 
510 	  g_object_unref (layout);
511 
512 	  other_dimension_size += 2;
513 	}
514 
515       requisition->width = other_dimension_size * spaces_per_row + (spaces_per_row - 1);
516       requisition->height = size * n_rows + (n_rows - 1);
517     }
518 
519   if (pager->priv->shadow_type != GTK_SHADOW_NONE)
520     {
521       GtkStyle *style;
522 
523       style = gtk_widget_get_style (widget);
524 
525       requisition->width += 2 * style->xthickness;
526       requisition->height += 2 * style->ythickness;
527     }
528 
529   gtk_widget_style_get (widget,
530 			"focus-line-width", &focus_width,
531 			NULL);
532 
533   requisition->width  += 2 * focus_width;
534   requisition->height += 2 * focus_width;
535 }
536 
537 static void
wnck_pager_size_allocate(GtkWidget * widget,GtkAllocation * allocation)538 wnck_pager_size_allocate (GtkWidget      *widget,
539                           GtkAllocation  *allocation)
540 {
541   WnckPager *pager;
542   int workspace_size;
543   int focus_width;
544   int width;
545   int height;
546 
547   pager = WNCK_PAGER (widget);
548 
549   gtk_widget_style_get (GTK_WIDGET (pager),
550 			"focus-line-width", &focus_width,
551 			NULL);
552 
553   width  = allocation->width  - 2 * focus_width;
554   height = allocation->height - 2* focus_width;
555 
556   if (pager->priv->shadow_type != GTK_SHADOW_NONE)
557     {
558       GtkStyle *style;
559 
560       style = gtk_widget_get_style (widget);
561 
562       width  -= 2 * style->xthickness;
563       height -= 2 * style->ythickness;
564     }
565 
566   g_assert (pager->priv->n_rows > 0);
567 
568   if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL)
569     {
570       if (pager->priv->show_all_workspaces)
571 	workspace_size = (width - (pager->priv->n_rows - 1))  / pager->priv->n_rows;
572       else
573 	workspace_size = width;
574     }
575   else
576     {
577       if (pager->priv->show_all_workspaces)
578 	workspace_size = (height - (pager->priv->n_rows - 1))/ pager->priv->n_rows;
579       else
580 	workspace_size = height;
581     }
582 
583   if (workspace_size != pager->priv->workspace_size)
584     {
585       pager->priv->workspace_size = workspace_size;
586       gtk_widget_queue_resize (GTK_WIDGET (widget));
587       return;
588     }
589 
590   GTK_WIDGET_CLASS (wnck_pager_parent_class)->size_allocate (widget,
591                                                              allocation);
592 }
593 
594 static void
get_workspace_rect(WnckPager * pager,int space,GdkRectangle * rect)595 get_workspace_rect (WnckPager    *pager,
596                     int           space,
597                     GdkRectangle *rect)
598 {
599   int hsize, vsize;
600   int n_spaces;
601   int spaces_per_row;
602   GtkWidget *widget;
603   int col, row;
604   GtkAllocation allocation;
605   GtkStyle *style;
606   int focus_width;
607 
608   widget = GTK_WIDGET (pager);
609 
610   gtk_widget_get_allocation (widget, &allocation);
611 
612   style = gtk_widget_get_style (widget);
613   gtk_widget_style_get (widget,
614 			"focus-line-width", &focus_width,
615 			NULL);
616 
617   if (!pager->priv->show_all_workspaces)
618     {
619       WnckWorkspace *active_space;
620 
621       active_space = wnck_screen_get_active_workspace (pager->priv->screen);
622 
623       if (active_space && space == wnck_workspace_get_number (active_space))
624 	{
625 	  rect->x = focus_width;
626 	  rect->y = focus_width;
627 	  rect->width = allocation.width - 2 * focus_width;
628 	  rect->height = allocation.height - 2 * focus_width;
629 
630 	  if (pager->priv->shadow_type != GTK_SHADOW_NONE)
631 	    {
632 	      rect->x += style->xthickness;
633 	      rect->y += style->ythickness;
634 	      rect->width -= 2 * style->xthickness;
635 	      rect->height -= 2 * style->ythickness;
636 	    }
637 	}
638       else
639 	{
640 	  rect->x = 0;
641 	  rect->y = 0;
642 	  rect->width = 0;
643 	  rect->height = 0;
644 	}
645 
646       return;
647     }
648 
649   hsize = allocation.width - 2 * focus_width;
650   vsize = allocation.height - 2 * focus_width;
651 
652   if (pager->priv->shadow_type != GTK_SHADOW_NONE)
653     {
654       hsize -= 2 * style->xthickness;
655       vsize -= 2 * style->ythickness;
656     }
657 
658   n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
659 
660   g_assert (pager->priv->n_rows > 0);
661   spaces_per_row = (n_spaces + pager->priv->n_rows - 1) / pager->priv->n_rows;
662 
663   if (pager->priv->orientation == GTK_ORIENTATION_VERTICAL)
664     {
665       rect->width = (hsize - (pager->priv->n_rows - 1)) / pager->priv->n_rows;
666       rect->height = (vsize - (spaces_per_row - 1)) / spaces_per_row;
667 
668       col = space / spaces_per_row;
669       row = space % spaces_per_row;
670 
671       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
672         col = pager->priv->n_rows - col - 1;
673 
674       rect->x = (rect->width + 1) * col;
675       rect->y = (rect->height + 1) * row;
676 
677       if (col == pager->priv->n_rows - 1)
678 	rect->width = hsize - rect->x;
679 
680       if (row  == spaces_per_row - 1)
681 	rect->height = vsize - rect->y;
682     }
683   else
684     {
685       rect->width = (hsize - (spaces_per_row - 1)) / spaces_per_row;
686       rect->height = (vsize - (pager->priv->n_rows - 1)) / pager->priv->n_rows;
687 
688       col = space % spaces_per_row;
689       row = space / spaces_per_row;
690 
691       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
692         col = spaces_per_row - col - 1;
693 
694       rect->x = (rect->width + 1) * col;
695       rect->y = (rect->height + 1) * row;
696 
697       if (col == spaces_per_row - 1)
698 	rect->width = hsize - rect->x;
699 
700       if (row  == pager->priv->n_rows - 1)
701 	rect->height = vsize - rect->y;
702     }
703 
704   rect->x += focus_width;
705   rect->y += focus_width;
706 
707   if (pager->priv->shadow_type != GTK_SHADOW_NONE)
708     {
709       rect->x += style->xthickness;
710       rect->y += style->ythickness;
711     }
712 }
713 
714 static gboolean
wnck_pager_window_state_is_relevant(int state)715 wnck_pager_window_state_is_relevant (int state)
716 {
717   return (state & (WNCK_WINDOW_STATE_HIDDEN | WNCK_WINDOW_STATE_SKIP_PAGER)) ? FALSE : TRUE;
718 }
719 
720 static gint
wnck_pager_window_get_workspace(WnckWindow * window,gboolean is_state_relevant)721 wnck_pager_window_get_workspace (WnckWindow *window,
722                                  gboolean    is_state_relevant)
723 {
724   gint state;
725   WnckWorkspace *workspace;
726 
727   state = wnck_window_get_state (window);
728   if (is_state_relevant && !wnck_pager_window_state_is_relevant (state))
729     return -1;
730   workspace = wnck_window_get_workspace (window);
731   if (workspace == NULL && wnck_window_is_pinned (window))
732     workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window));
733 
734   return workspace ? wnck_workspace_get_number (workspace) : -1;
735 }
736 
737 static GList*
get_windows_for_workspace_in_bottom_to_top(WnckScreen * screen,WnckWorkspace * workspace)738 get_windows_for_workspace_in_bottom_to_top (WnckScreen    *screen,
739                                             WnckWorkspace *workspace)
740 {
741   GList *result;
742   GList *windows;
743   GList *tmp;
744   int workspace_num;
745 
746   result = NULL;
747   workspace_num = wnck_workspace_get_number (workspace);
748 
749   windows = wnck_screen_get_windows_stacked (screen);
750   for (tmp = windows; tmp != NULL; tmp = tmp->next)
751     {
752       WnckWindow *win = WNCK_WINDOW (tmp->data);
753       if (wnck_pager_window_get_workspace (win, TRUE) == workspace_num)
754 	result = g_list_prepend (result, win);
755     }
756 
757   result = g_list_reverse (result);
758 
759   return result;
760 }
761 
762 static void
get_window_rect(WnckWindow * window,const GdkRectangle * workspace_rect,GdkRectangle * rect)763 get_window_rect (WnckWindow         *window,
764                  const GdkRectangle *workspace_rect,
765                  GdkRectangle       *rect)
766 {
767   double width_ratio, height_ratio;
768   int x, y, width, height;
769   WnckWorkspace *workspace;
770   GdkRectangle unclipped_win_rect;
771 
772   workspace = wnck_window_get_workspace (window);
773   if (workspace == NULL)
774     workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window));
775 
776   /* scale window down by same ratio we scaled workspace down */
777   width_ratio = (double) workspace_rect->width / (double) wnck_workspace_get_width (workspace);
778   height_ratio = (double) workspace_rect->height / (double) wnck_workspace_get_height (workspace);
779 
780   wnck_window_get_geometry (window, &x, &y, &width, &height);
781 
782   x += wnck_workspace_get_viewport_x (workspace);
783   y += wnck_workspace_get_viewport_y (workspace);
784   x = x * width_ratio + 0.5;
785   y = y * height_ratio + 0.5;
786   width = width * width_ratio + 0.5;
787   height = height * height_ratio + 0.5;
788 
789   x += workspace_rect->x;
790   y += workspace_rect->y;
791 
792   if (width < 3)
793     width = 3;
794   if (height < 3)
795     height = 3;
796 
797   unclipped_win_rect.x = x;
798   unclipped_win_rect.y = y;
799   unclipped_win_rect.width = width;
800   unclipped_win_rect.height = height;
801 
802   gdk_rectangle_intersect ((GdkRectangle *) workspace_rect, &unclipped_win_rect, rect);
803 }
804 
805 static void
draw_window(GdkDrawable * drawable,GtkWidget * widget,WnckWindow * win,const GdkRectangle * winrect,GtkStateType state,gboolean translucent)806 draw_window (GdkDrawable        *drawable,
807              GtkWidget          *widget,
808              WnckWindow         *win,
809              const GdkRectangle *winrect,
810              GtkStateType        state,
811              gboolean            translucent)
812 {
813   GtkStyle *style;
814   cairo_t *cr;
815   GdkPixbuf *icon;
816   int icon_x, icon_y, icon_w, icon_h;
817   gboolean is_active;
818   GdkColor *color;
819   gdouble translucency;
820 
821   style = gtk_widget_get_style (widget);
822 
823   is_active = wnck_window_is_active (win);
824   translucency = translucent ? 0.4 : 1.0;
825 
826   cr = gdk_cairo_create (drawable);
827   cairo_rectangle (cr, winrect->x, winrect->y, winrect->width, winrect->height);
828   cairo_clip (cr);
829 
830   if (is_active)
831     color = &style->light[state];
832   else
833     color = &style->bg[state];
834   cairo_set_source_rgba (cr,
835                          color->red / 65535.,
836                          color->green / 65535.,
837                          color->blue / 65535.,
838                          translucency);
839   cairo_rectangle (cr,
840                    winrect->x + 1, winrect->y + 1,
841                    MAX (0, winrect->width - 2), MAX (0, winrect->height - 2));
842   cairo_fill (cr);
843 
844   icon = wnck_window_get_icon (win);
845 
846   icon_w = icon_h = 0;
847 
848   if (icon)
849     {
850       icon_w = gdk_pixbuf_get_width (icon);
851       icon_h = gdk_pixbuf_get_height (icon);
852 
853       /* If the icon is too big, fall back to mini icon.
854        * We don't arbitrarily scale the icon, because it's
855        * just too slow on my Athlon 850.
856        */
857       if (icon_w > (winrect->width - 2) ||
858           icon_h > (winrect->height - 2))
859         {
860           icon = wnck_window_get_mini_icon (win);
861           if (icon)
862             {
863               icon_w = gdk_pixbuf_get_width (icon);
864               icon_h = gdk_pixbuf_get_height (icon);
865 
866               /* Give up. */
867               if (icon_w > (winrect->width - 2) ||
868                   icon_h > (winrect->height - 2))
869                 icon = NULL;
870             }
871         }
872     }
873 
874   if (icon)
875     {
876       icon_x = winrect->x + (winrect->width - icon_w) / 2;
877       icon_y = winrect->y + (winrect->height - icon_h) / 2;
878 
879       cairo_save (cr);
880       gdk_cairo_set_source_pixbuf (cr, icon, icon_x, icon_y);
881       cairo_rectangle (cr, icon_x, icon_y, icon_w, icon_h);
882       cairo_clip (cr);
883       cairo_paint_with_alpha (cr, translucency);
884       cairo_restore (cr);
885     }
886 
887   if (is_active)
888     color = &style->fg[state];
889   else
890     color = &style->fg[state];
891   cairo_set_source_rgba (cr,
892                          color->red / 65535.,
893                          color->green / 65535.,
894                          color->blue / 65535.,
895                          translucency);
896   cairo_set_line_width (cr, 1.0);
897   cairo_rectangle (cr,
898                    winrect->x + 0.5, winrect->y + 0.5,
899                    MAX (0, winrect->width - 1), MAX (0, winrect->height - 1));
900   cairo_stroke (cr);
901 
902   cairo_destroy (cr);
903 }
904 
905 static WnckWindow *
window_at_point(WnckPager * pager,WnckWorkspace * space,GdkRectangle * space_rect,int x,int y)906 window_at_point (WnckPager     *pager,
907                  WnckWorkspace *space,
908                  GdkRectangle  *space_rect,
909                  int            x,
910                  int            y)
911 {
912   WnckWindow *window;
913   GList *windows;
914   GList *tmp;
915 
916   window = NULL;
917 
918   windows = get_windows_for_workspace_in_bottom_to_top (pager->priv->screen,
919                                                         space);
920 
921   /* clicks on top windows first */
922   windows = g_list_reverse (windows);
923 
924   for (tmp = windows; tmp != NULL; tmp = tmp->next)
925     {
926       WnckWindow *win = WNCK_WINDOW (tmp->data);
927       GdkRectangle winrect;
928 
929       get_window_rect (win, space_rect, &winrect);
930 
931       if (POINT_IN_RECT (x, y, winrect))
932         {
933           /* wnck_window_activate (win); */
934           window = win;
935           break;
936         }
937     }
938 
939   g_list_free (windows);
940 
941   return window;
942 }
943 
944 static int
workspace_at_point(WnckPager * pager,int x,int y,int * viewport_x,int * viewport_y)945 workspace_at_point (WnckPager *pager,
946                     int        x,
947                     int        y,
948                     int       *viewport_x,
949                     int       *viewport_y)
950 {
951   GtkWidget *widget;
952   int i;
953   int n_spaces;
954   GtkAllocation allocation;
955   int focus_width;
956   int xthickness;
957   int ythickness;
958 
959   widget = GTK_WIDGET (pager);
960 
961   gtk_widget_get_allocation (widget, &allocation);
962 
963   gtk_widget_style_get (GTK_WIDGET (pager),
964 			"focus-line-width", &focus_width,
965 			NULL);
966 
967   if (pager->priv->shadow_type != GTK_SHADOW_NONE)
968     {
969       GtkStyle *style;
970 
971       style = gtk_widget_get_style (widget);
972 
973       xthickness = focus_width + style->xthickness;
974       ythickness = focus_width + style->ythickness;
975     }
976   else
977     {
978       xthickness = focus_width;
979       ythickness = focus_width;
980     }
981 
982   n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
983 
984   i = 0;
985   while (i < n_spaces)
986     {
987       GdkRectangle rect;
988 
989       get_workspace_rect (pager, i, &rect);
990 
991       /* If workspace is on the edge, pretend points on the frame belong to the
992        * workspace.
993        * Else, pretend the right/bottom line separating two workspaces belong
994        * to the workspace.
995        */
996 
997       if (rect.x == xthickness)
998         {
999           rect.x = 0;
1000           rect.width += xthickness;
1001         }
1002       if (rect.y == ythickness)
1003         {
1004           rect.y = 0;
1005           rect.height += ythickness;
1006         }
1007       if (rect.y + rect.height == allocation.height - ythickness)
1008         {
1009           rect.height += ythickness;
1010         }
1011       else
1012         {
1013           rect.height += 1;
1014         }
1015       if (rect.x + rect.width == allocation.width - xthickness)
1016         {
1017           rect.width += xthickness;
1018         }
1019       else
1020         {
1021           rect.width += 1;
1022         }
1023 
1024       if (POINT_IN_RECT (x, y, rect))
1025         {
1026 	  double width_ratio, height_ratio;
1027 	  WnckWorkspace *space;
1028 
1029 	  space = wnck_screen_get_workspace (pager->priv->screen, i);
1030           g_assert (space != NULL);
1031 
1032           /* Scale x, y mouse coords to corresponding screenwide viewport coords */
1033 
1034           width_ratio = (double) wnck_workspace_get_width (space) / (double) rect.width;
1035           height_ratio = (double) wnck_workspace_get_height (space) / (double) rect.height;
1036 
1037           if (viewport_x)
1038             *viewport_x = width_ratio * (x - rect.x);
1039           if (viewport_y)
1040             *viewport_y = height_ratio * (y - rect.y);
1041 
1042 	  return i;
1043 	}
1044 
1045       ++i;
1046     }
1047 
1048   return -1;
1049 }
1050 
1051 
1052 static void
wnck_pager_draw_workspace(WnckPager * pager,int workspace,GdkRectangle * rect,GdkPixbuf * bg_pixbuf)1053 wnck_pager_draw_workspace (WnckPager    *pager,
1054 			   int           workspace,
1055 			   GdkRectangle *rect,
1056                            GdkPixbuf    *bg_pixbuf)
1057 {
1058   GList *windows;
1059   GList *tmp;
1060   gboolean is_current;
1061   WnckWorkspace *space;
1062   GtkWidget *widget;
1063   GtkStateType state;
1064   GdkWindow *window;
1065   GtkStyle *style;
1066 
1067   space = wnck_screen_get_workspace (pager->priv->screen, workspace);
1068   if (!space)
1069     return;
1070 
1071   widget = GTK_WIDGET (pager);
1072   is_current = (space == wnck_screen_get_active_workspace (pager->priv->screen));
1073 
1074   if (is_current)
1075     state = GTK_STATE_SELECTED;
1076   else if (workspace == pager->priv->prelight)
1077     state = GTK_STATE_PRELIGHT;
1078   else
1079     state = GTK_STATE_NORMAL;
1080 
1081   window = gtk_widget_get_window (widget);
1082   style = gtk_widget_get_style (widget);
1083 
1084   /* FIXME in names mode, should probably draw things like a button.
1085    */
1086 
1087   if (bg_pixbuf)
1088     {
1089       gdk_draw_pixbuf (window,
1090                        style->dark_gc[state],
1091                        bg_pixbuf,
1092                        0, 0,
1093                        rect->x, rect->y,
1094                        -1, -1,
1095                        GDK_RGB_DITHER_MAX,
1096                        0, 0);
1097     }
1098   else
1099     {
1100       cairo_t *cr;
1101 
1102       cr = gdk_cairo_create (window);
1103 
1104       if (!wnck_workspace_is_virtual (space))
1105         {
1106           gdk_cairo_set_source_color (cr, &style->dark[state]);
1107           cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
1108           cairo_fill (cr);
1109         }
1110       else
1111         {
1112           //FIXME prelight for dnd in the viewport?
1113           int workspace_width, workspace_height;
1114           int screen_width, screen_height;
1115           double width_ratio, height_ratio;
1116           double vx, vy, vw, vh; /* viewport */
1117 
1118           workspace_width = wnck_workspace_get_width (space);
1119           workspace_height = wnck_workspace_get_height (space);
1120           screen_width = wnck_screen_get_width (pager->priv->screen);
1121           screen_height = wnck_screen_get_height (pager->priv->screen);
1122 
1123           if ((workspace_width % screen_width == 0) &&
1124               (workspace_height % screen_height == 0))
1125             {
1126               int i, j;
1127               int active_i, active_j;
1128               int horiz_views;
1129               int verti_views;
1130 
1131               horiz_views = workspace_width / screen_width;
1132               verti_views = workspace_height / screen_height;
1133 
1134               /* do not forget thin lines to delimit "workspaces" */
1135               width_ratio = (rect->width - (horiz_views - 1)) /
1136                             (double) workspace_width;
1137               height_ratio = (rect->height - (verti_views - 1)) /
1138                              (double) workspace_height;
1139 
1140               if (is_current)
1141                 {
1142                   active_i =
1143                     wnck_workspace_get_viewport_x (space) / screen_width;
1144                   active_j =
1145                     wnck_workspace_get_viewport_y (space) / screen_height;
1146                 }
1147               else
1148                 {
1149                   active_i = -1;
1150                   active_j = -1;
1151                 }
1152 
1153               for (i = 0; i < horiz_views; i++)
1154                 {
1155                   /* "+ i" is for the thin lines */
1156                   vx = rect->x + (width_ratio * screen_width) * i + i;
1157 
1158                   if (i == horiz_views - 1)
1159                     vw = rect->width + rect->x - vx;
1160                   else
1161                     vw = width_ratio * screen_width;
1162 
1163                   vh = height_ratio * screen_height;
1164 
1165                   for (j = 0; j < verti_views; j++)
1166                     {
1167                       /* "+ j" is for the thin lines */
1168                       vy = rect->y + (height_ratio * screen_height) * j + j;
1169 
1170                       if (j == verti_views - 1)
1171                         vh = rect->height + rect->y - vy;
1172 
1173                       if (active_i == i && active_j == j)
1174                         gdk_cairo_set_source_color (cr,
1175                                                     &style->dark[GTK_STATE_SELECTED]);
1176                       else
1177                         gdk_cairo_set_source_color (cr,
1178                                                     &style->dark[GTK_STATE_NORMAL]);
1179                       cairo_rectangle (cr, vx, vy, vw, vh);
1180                       cairo_fill (cr);
1181                     }
1182                 }
1183             }
1184           else
1185             {
1186               width_ratio = rect->width / (double) workspace_width;
1187               height_ratio = rect->height / (double) workspace_height;
1188 
1189               /* first draw non-active part of the viewport */
1190               gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]);
1191               cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
1192               cairo_fill (cr);
1193 
1194               if (is_current)
1195                 {
1196                   /* draw the active part of the viewport */
1197                   vx = rect->x +
1198                     width_ratio * wnck_workspace_get_viewport_x (space);
1199                   vy = rect->y +
1200                     height_ratio * wnck_workspace_get_viewport_y (space);
1201                   vw = width_ratio * screen_width;
1202                   vh = height_ratio * screen_height;
1203 
1204                   gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_SELECTED]);
1205                   cairo_rectangle (cr, vx, vy, vw, vh);
1206                   cairo_fill (cr);
1207                 }
1208             }
1209         }
1210 
1211       cairo_destroy (cr);
1212     }
1213 
1214   if (pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT)
1215     {
1216       windows = get_windows_for_workspace_in_bottom_to_top (pager->priv->screen,
1217 							    wnck_screen_get_workspace (pager->priv->screen,
1218 										       workspace));
1219 
1220       tmp = windows;
1221       while (tmp != NULL)
1222 	{
1223 	  WnckWindow *win = tmp->data;
1224 	  GdkRectangle winrect;
1225 
1226 	  get_window_rect (win, rect, &winrect);
1227 
1228 	  draw_window (window,
1229 		       widget,
1230 		       win,
1231 		       &winrect,
1232                        state,
1233 		       win == pager->priv->drag_window && pager->priv->dragging ? TRUE : FALSE);
1234 
1235 	  tmp = tmp->next;
1236 	}
1237 
1238       g_list_free (windows);
1239     }
1240   else
1241     {
1242       /* Workspace name mode */
1243       const char *workspace_name;
1244       PangoLayout *layout;
1245       int w, h;
1246 
1247       workspace_name = wnck_workspace_get_name (wnck_screen_get_workspace (pager->priv->screen,
1248 									   workspace));
1249       layout = gtk_widget_create_pango_layout  (widget,
1250 						workspace_name);
1251 
1252       pango_layout_get_pixel_size (layout, &w, &h);
1253 
1254       gdk_draw_layout  (window,
1255 			is_current ?
1256 			style->fg_gc[GTK_STATE_SELECTED] :
1257 			style->fg_gc[GTK_STATE_NORMAL],
1258 			rect->x + (rect->width - w) / 2,
1259 			rect->y + (rect->height - h) / 2,
1260 			layout);
1261 
1262       g_object_unref (layout);
1263     }
1264 
1265   if (workspace == pager->priv->prelight && pager->priv->prelight_dnd)
1266     {
1267       /* stolen directly from gtk source so it matches nicely */
1268       cairo_t *cr;
1269       gtk_paint_shadow (style, window,
1270 		        GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1271 		        NULL, widget, "dnd",
1272 			rect->x, rect->y, rect->width, rect->height);
1273 
1274       cr = gdk_cairo_create (window);
1275       cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
1276       cairo_set_line_width (cr, 1.0);
1277       cairo_rectangle (cr,
1278 		       rect->x + 0.5, rect->y + 0.5,
1279 		       MAX (0, rect->width - 1), MAX (0, rect->height - 1));
1280       cairo_stroke (cr);
1281       cairo_destroy (cr);
1282     }
1283 }
1284 
1285 static gboolean
wnck_pager_expose_event(GtkWidget * widget,GdkEventExpose * event)1286 wnck_pager_expose_event  (GtkWidget      *widget,
1287                           GdkEventExpose *event)
1288 {
1289   WnckPager *pager;
1290   int i;
1291   int n_spaces;
1292   WnckWorkspace *active_space;
1293   GdkPixbuf *bg_pixbuf;
1294   gboolean first;
1295   GdkWindow *window;
1296   GtkAllocation allocation;
1297   GtkStyle *style;
1298   int focus_width;
1299 
1300   pager = WNCK_PAGER (widget);
1301 
1302   n_spaces = wnck_screen_get_workspace_count (pager->priv->screen);
1303   active_space = wnck_screen_get_active_workspace (pager->priv->screen);
1304   bg_pixbuf = NULL;
1305   first = TRUE;
1306 
1307   window = gtk_widget_get_window (widget);
1308   gtk_widget_get_allocation (widget, &allocation);
1309 
1310   style = gtk_widget_get_style (widget);
1311   gtk_widget_style_get (widget,
1312 			"focus-line-width", &focus_width,
1313 			NULL);
1314 
1315   if (gtk_widget_has_focus (widget))
1316     gtk_paint_focus (style,
1317 		     window,
1318 		     gtk_widget_get_state (widget),
1319 		     NULL,
1320 		     widget,
1321 		     "pager",
1322 		     0, 0,
1323 		     allocation.width,
1324 		     allocation.height);
1325 
1326   if (pager->priv->shadow_type != GTK_SHADOW_NONE)
1327     {
1328       gtk_paint_shadow (style,
1329 			window,
1330 			gtk_widget_get_state (widget),
1331 			pager->priv->shadow_type,
1332 			NULL,
1333 			widget,
1334 			"pager",
1335 			focus_width,
1336 			focus_width,
1337 			allocation.width - 2 * focus_width,
1338 			allocation.height - 2 * focus_width);
1339     }
1340 
1341   i = 0;
1342   while (i < n_spaces)
1343     {
1344       GdkRectangle rect, intersect;
1345 
1346       if (pager->priv->show_all_workspaces ||
1347 	  (active_space && i == wnck_workspace_get_number (active_space)))
1348 	{
1349 	  get_workspace_rect (pager, i, &rect);
1350 
1351           /* We only want to do this once, even if w/h change,
1352            * for efficiency. width/height will only change by
1353            * one pixel at most.
1354            */
1355           if (first &&
1356               pager->priv->display_mode == WNCK_PAGER_DISPLAY_CONTENT)
1357             {
1358               bg_pixbuf = wnck_pager_get_background (pager,
1359                                                      rect.width,
1360                                                      rect.height);
1361               first = FALSE;
1362             }
1363 
1364 	  if (gdk_rectangle_intersect (&event->area, &rect, &intersect))
1365 	    wnck_pager_draw_workspace (pager, i, &rect, bg_pixbuf);
1366 	}
1367 
1368       ++i;
1369     }
1370 
1371   return FALSE;
1372 }
1373 
1374 static gboolean
wnck_pager_button_press(GtkWidget * widget,GdkEventButton * event)1375 wnck_pager_button_press (GtkWidget      *widget,
1376                          GdkEventButton *event)
1377 {
1378   WnckPager *pager;
1379   int space_number;
1380   WnckWorkspace *space = NULL;
1381   GdkRectangle workspace_rect;
1382 
1383   if (event->button != 1)
1384     return FALSE;
1385 
1386   pager = WNCK_PAGER (widget);
1387 
1388   space_number = workspace_at_point (pager, event->x, event->y, NULL, NULL);
1389 
1390   if (space_number != -1)
1391   {
1392     get_workspace_rect (pager, space_number, &workspace_rect);
1393     space = wnck_screen_get_workspace (pager->priv->screen, space_number);
1394   }
1395 
1396   if (space)
1397     {
1398       /* always save the start coordinates so we can know if we need to change
1399        * workspace when the button is released (ie, start and end coordinates
1400        * should be in the same workspace) */
1401       pager->priv->drag_start_x = event->x;
1402       pager->priv->drag_start_y = event->y;
1403     }
1404 
1405   if (space && (pager->priv->display_mode != WNCK_PAGER_DISPLAY_NAME))
1406     {
1407       pager->priv->drag_window = window_at_point (pager, space,
1408                                                   &workspace_rect,
1409                                                   event->x, event->y);
1410     }
1411 
1412   return TRUE;
1413 }
1414 
1415 static gboolean
wnck_pager_drag_motion_timeout(gpointer data)1416 wnck_pager_drag_motion_timeout (gpointer data)
1417 {
1418   WnckPager *pager = WNCK_PAGER (data);
1419   WnckWorkspace *active_workspace, *dnd_workspace;
1420 
1421   pager->priv->dnd_activate = 0;
1422   active_workspace = wnck_screen_get_active_workspace (pager->priv->screen);
1423   dnd_workspace    = wnck_screen_get_workspace (pager->priv->screen,
1424                                                 pager->priv->prelight);
1425 
1426   if (dnd_workspace &&
1427       (pager->priv->prelight != wnck_workspace_get_number (active_workspace)))
1428     wnck_workspace_activate (dnd_workspace, pager->priv->dnd_time);
1429 
1430   return FALSE;
1431 }
1432 
1433 static void
wnck_pager_queue_draw_workspace(WnckPager * pager,gint i)1434 wnck_pager_queue_draw_workspace (WnckPager *pager,
1435                                  gint       i)
1436 {
1437   GdkRectangle rect;
1438 
1439   if (i < 0)
1440     return;
1441 
1442   get_workspace_rect (pager, i, &rect);
1443   gtk_widget_queue_draw_area (GTK_WIDGET (pager),
1444                               rect.x, rect.y,
1445 			      rect.width, rect.height);
1446 }
1447 
1448 static void
wnck_pager_queue_draw_window(WnckPager * pager,WnckWindow * window)1449 wnck_pager_queue_draw_window (WnckPager  *pager,
1450                               WnckWindow *window)
1451 {
1452   gint workspace;
1453 
1454   workspace = wnck_pager_window_get_workspace (window, TRUE);
1455   if (workspace == -1)
1456     return;
1457 
1458   wnck_pager_queue_draw_workspace (pager, workspace);
1459 }
1460 
1461 static void
wnck_pager_check_prelight(WnckPager * pager,gint x,gint y,gboolean prelight_dnd)1462 wnck_pager_check_prelight (WnckPager *pager,
1463                            gint       x,
1464                            gint       y,
1465                            gboolean   prelight_dnd)
1466 {
1467   gint id;
1468 
1469   if (x < 0 || y < 0)
1470     id = -1;
1471   else
1472     id = workspace_at_point (pager, x, y, NULL, NULL);
1473 
1474   if (id != pager->priv->prelight)
1475     {
1476       wnck_pager_queue_draw_workspace (pager, pager->priv->prelight);
1477       wnck_pager_queue_draw_workspace (pager, id);
1478       pager->priv->prelight = id;
1479       pager->priv->prelight_dnd = prelight_dnd;
1480     }
1481   else if (prelight_dnd != pager->priv->prelight_dnd)
1482     {
1483       wnck_pager_queue_draw_workspace (pager, pager->priv->prelight);
1484       pager->priv->prelight_dnd = prelight_dnd;
1485     }
1486 }
1487 
1488 static gboolean
wnck_pager_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)1489 wnck_pager_drag_motion (GtkWidget          *widget,
1490                         GdkDragContext     *context,
1491                         gint                x,
1492                         gint                y,
1493                         guint               time)
1494 {
1495   WnckPager *pager;
1496   gint previous_workspace;
1497 
1498   pager = WNCK_PAGER (widget);
1499 
1500   previous_workspace = pager->priv->prelight;
1501   wnck_pager_check_prelight (pager, x, y, TRUE);
1502 
1503   if (gtk_drag_dest_find_target (widget, context, NULL))
1504     {
1505 #if GTK_CHECK_VERSION(2,21,0)
1506        gdk_drag_status (context,
1507                         gdk_drag_context_get_suggested_action (context), time);
1508 #else
1509        gdk_drag_status (context, context->suggested_action, time);
1510 #endif
1511     }
1512   else
1513     {
1514       gdk_drag_status (context, 0, time);
1515 
1516       if (pager->priv->prelight != previous_workspace &&
1517 	  pager->priv->dnd_activate != 0)
1518 	{
1519 	  /* remove timeout, the window we hover over changed */
1520 	  g_source_remove (pager->priv->dnd_activate);
1521 	  pager->priv->dnd_activate = 0;
1522 	  pager->priv->dnd_time = 0;
1523 	}
1524 
1525       if (pager->priv->dnd_activate == 0 && pager->priv->prelight > -1)
1526 	{
1527 	  pager->priv->dnd_activate = g_timeout_add (WNCK_ACTIVATE_TIMEOUT,
1528                                                      wnck_pager_drag_motion_timeout,
1529                                                      pager);
1530 	  pager->priv->dnd_time = time;
1531 	}
1532     }
1533 
1534   return (pager->priv->prelight != -1);
1535 }
1536 
1537 static gboolean
wnck_pager_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)1538 wnck_pager_drag_drop  (GtkWidget        *widget,
1539 		       GdkDragContext   *context,
1540 		       gint              x,
1541 		       gint              y,
1542 		       guint             time)
1543 {
1544   WnckPager *pager = WNCK_PAGER (widget);
1545   GdkAtom target;
1546 
1547   target = gtk_drag_dest_find_target (widget, context, NULL);
1548 
1549   if (target != GDK_NONE)
1550     gtk_drag_get_data (widget, context, target, time);
1551   else
1552     gtk_drag_finish (context, FALSE, FALSE, time);
1553 
1554   wnck_pager_clear_drag (pager);
1555   wnck_pager_check_prelight (pager, x, y, FALSE);
1556 
1557   return TRUE;
1558 }
1559 
1560 static void
wnck_pager_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)1561 wnck_pager_drag_data_received (GtkWidget          *widget,
1562 	  	               GdkDragContext     *context,
1563 			       gint                x,
1564 			       gint                y,
1565 			       GtkSelectionData   *selection_data,
1566 			       guint               info,
1567 			       guint               time)
1568 {
1569   WnckPager *pager = WNCK_PAGER (widget);
1570   WnckWorkspace *space;
1571   GList *tmp;
1572   gint i;
1573   gulong xid;
1574 
1575   if ((gtk_selection_data_get_length (selection_data) != sizeof (gulong)) ||
1576       (gtk_selection_data_get_format (selection_data) != 8))
1577     {
1578       gtk_drag_finish (context, FALSE, FALSE, time);
1579       return;
1580     }
1581 
1582   i = workspace_at_point (pager, x, y, NULL, NULL);
1583   space = wnck_screen_get_workspace (pager->priv->screen, i);
1584   if (!space)
1585     {
1586       gtk_drag_finish (context, FALSE, FALSE, time);
1587       return;
1588     }
1589 
1590   xid = *((gulong *) gtk_selection_data_get_data (selection_data));
1591 
1592   for (tmp = wnck_screen_get_windows_stacked (pager->priv->screen); tmp != NULL; tmp = tmp->next)
1593     {
1594       if (wnck_window_get_xid (tmp->data) == xid)
1595 	{
1596 	  WnckWindow *win = tmp->data;
1597 	  wnck_window_move_to_workspace (win, space);
1598 	  if (space == wnck_screen_get_active_workspace (pager->priv->screen))
1599 	    wnck_window_activate (win, time);
1600 	  gtk_drag_finish (context, TRUE, FALSE, time);
1601 	  return;
1602 	}
1603     }
1604 
1605   gtk_drag_finish (context, FALSE, FALSE, time);
1606 }
1607 
1608 static void
wnck_pager_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)1609 wnck_pager_drag_data_get (GtkWidget        *widget,
1610                           GdkDragContext   *context,
1611                           GtkSelectionData *selection_data,
1612                           guint             info,
1613                           guint             time)
1614 {
1615   WnckPager *pager = WNCK_PAGER (widget);
1616   gulong xid;
1617 
1618   if (pager->priv->drag_window == NULL)
1619     return;
1620 
1621   xid = wnck_window_get_xid (pager->priv->drag_window);
1622   gtk_selection_data_set (selection_data,
1623 			  gtk_selection_data_get_target (selection_data),
1624 			  8, (guchar *)&xid, sizeof (gulong));
1625 }
1626 
1627 static void
wnck_pager_drag_end(GtkWidget * widget,GdkDragContext * context)1628 wnck_pager_drag_end (GtkWidget        *widget,
1629                      GdkDragContext   *context)
1630 {
1631   WnckPager *pager = WNCK_PAGER (widget);
1632 
1633   wnck_pager_clear_drag (pager);
1634 }
1635 
1636 static void
wnck_pager_drag_motion_leave(GtkWidget * widget,GdkDragContext * context,guint time)1637 wnck_pager_drag_motion_leave (GtkWidget          *widget,
1638                               GdkDragContext     *context,
1639                               guint               time)
1640 {
1641   WnckPager *pager = WNCK_PAGER (widget);
1642 
1643   if (pager->priv->dnd_activate != 0)
1644     {
1645       g_source_remove (pager->priv->dnd_activate);
1646       pager->priv->dnd_activate = 0;
1647     }
1648   pager->priv->dnd_time = 0;
1649   wnck_pager_check_prelight (pager, -1, -1, FALSE);
1650 }
1651 
1652 static void
1653 wnck_drag_clean_up (WnckWindow     *window,
1654 		    GdkDragContext *context,
1655 		    gboolean	    clean_up_for_context_destroy,
1656 		    gboolean	    clean_up_for_window_destroy);
1657 
1658 static void
wnck_drag_context_destroyed(gpointer windowp,GObject * context)1659 wnck_drag_context_destroyed (gpointer  windowp,
1660                              GObject  *context)
1661 {
1662   wnck_drag_clean_up (windowp, (GdkDragContext *) context, TRUE, FALSE);
1663 }
1664 
1665 static void
wnck_update_drag_icon(WnckWindow * window,GdkDragContext * context)1666 wnck_update_drag_icon (WnckWindow     *window,
1667 		       GdkDragContext *context)
1668 {
1669   gint org_w, org_h, dnd_w, dnd_h;
1670   WnckWorkspace *workspace;
1671   GdkRectangle rect;
1672   GdkPixmap *pixmap;
1673   GtkWidget *widget;
1674 
1675   widget = g_object_get_data (G_OBJECT (context), "wnck-drag-source-widget");
1676   if (!widget)
1677     return;
1678 
1679   if (!gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
1680 					  GTK_ICON_SIZE_DND, &dnd_w, &dnd_h))
1681     dnd_w = dnd_h = 32;
1682   /* windows are huge, so let's make this huge */
1683   dnd_w *= 3;
1684 
1685   workspace = wnck_window_get_workspace (window);
1686   if (workspace == NULL)
1687     workspace = wnck_screen_get_active_workspace (wnck_window_get_screen (window));
1688   if (workspace == NULL)
1689     return;
1690 
1691   wnck_window_get_geometry (window, NULL, NULL, &org_w, &org_h);
1692 
1693   rect.x = rect.y = 0;
1694   rect.width = 0.5 + ((double) (dnd_w * org_w) / (double) wnck_workspace_get_width (workspace));
1695   rect.width = MIN (org_w, rect.width);
1696   rect.height = 0.5 + ((double) (rect.width * org_h) / (double) org_w);
1697 
1698   /* we need at least three pixels to draw the smallest window */
1699   rect.width = MAX (rect.width, 3);
1700   rect.height = MAX (rect.height, 3);
1701 
1702   pixmap = gdk_pixmap_new (gtk_widget_get_window (widget),
1703                            rect.width, rect.height, -1);
1704   draw_window (GDK_DRAWABLE (pixmap), widget, window,
1705 	       &rect, GTK_STATE_NORMAL, FALSE);
1706 
1707   gtk_drag_set_icon_pixmap (context,
1708                             gdk_drawable_get_colormap (GDK_DRAWABLE (pixmap)),
1709 			    pixmap, NULL,
1710 			    -2, -2);
1711 
1712   g_object_unref (pixmap);
1713 }
1714 
1715 static void
wnck_drag_window_destroyed(gpointer contextp,GObject * window)1716 wnck_drag_window_destroyed (gpointer  contextp,
1717                             GObject  *window)
1718 {
1719   wnck_drag_clean_up ((WnckWindow *) window, GDK_DRAG_CONTEXT (contextp),
1720                       FALSE, TRUE);
1721 }
1722 
1723 static void
wnck_drag_source_destroyed(gpointer contextp,GObject * drag_source)1724 wnck_drag_source_destroyed (gpointer  contextp,
1725                             GObject  *drag_source)
1726 {
1727   g_object_steal_data (G_OBJECT (contextp), "wnck-drag-source-widget");
1728 }
1729 
1730 /* CAREFUL: This function is a bit brittle, because the pointers given may be
1731  * finalized already */
1732 static void
wnck_drag_clean_up(WnckWindow * window,GdkDragContext * context,gboolean clean_up_for_context_destroy,gboolean clean_up_for_window_destroy)1733 wnck_drag_clean_up (WnckWindow     *window,
1734 		    GdkDragContext *context,
1735 		    gboolean	    clean_up_for_context_destroy,
1736 		    gboolean	    clean_up_for_window_destroy)
1737 {
1738   if (clean_up_for_context_destroy)
1739     {
1740       GtkWidget *drag_source;
1741 
1742       drag_source = g_object_get_data (G_OBJECT (context),
1743                                        "wnck-drag-source-widget");
1744       if (drag_source)
1745         g_object_weak_unref (G_OBJECT (drag_source),
1746                              wnck_drag_source_destroyed, context);
1747 
1748       g_object_weak_unref (G_OBJECT (window),
1749                            wnck_drag_window_destroyed, context);
1750       if (g_signal_handlers_disconnect_by_func (window,
1751                                                 wnck_update_drag_icon, context) != 2)
1752 	g_assert_not_reached ();
1753     }
1754 
1755   if (clean_up_for_window_destroy)
1756     {
1757       g_object_steal_data (G_OBJECT (context), "wnck-drag-source-widget");
1758       g_object_weak_unref (G_OBJECT (context),
1759                            wnck_drag_context_destroyed, window);
1760     }
1761 }
1762 
1763 /**
1764  * wnck_window_set_as_drag_icon:
1765  * @window: #WnckWindow to use as drag icon
1766  * @context: #GdkDragContext to set the icon on
1767  * @drag_source: #GtkWidget that started the drag, or one of its parent. This
1768  * widget needs to stay alive as long as possible during the drag.
1769  *
1770  * Sets the given @window as the drag icon for @context.
1771  **/
1772 void
_wnck_window_set_as_drag_icon(WnckWindow * window,GdkDragContext * context,GtkWidget * drag_source)1773 _wnck_window_set_as_drag_icon (WnckWindow     *window,
1774                                GdkDragContext *context,
1775                                GtkWidget      *drag_source)
1776 {
1777   g_return_if_fail (WNCK_IS_WINDOW (window));
1778   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1779 
1780   g_object_weak_ref (G_OBJECT (window), wnck_drag_window_destroyed, context);
1781   g_signal_connect (window, "geometry_changed",
1782                     G_CALLBACK (wnck_update_drag_icon), context);
1783   g_signal_connect (window, "icon_changed",
1784                     G_CALLBACK (wnck_update_drag_icon), context);
1785 
1786   g_object_set_data (G_OBJECT (context), "wnck-drag-source-widget", drag_source);
1787   g_object_weak_ref (G_OBJECT (drag_source), wnck_drag_source_destroyed, context);
1788 
1789   g_object_weak_ref (G_OBJECT (context), wnck_drag_context_destroyed, window);
1790 
1791   wnck_update_drag_icon (window, context);
1792 }
1793 
1794 static gboolean
wnck_pager_motion(GtkWidget * widget,GdkEventMotion * event)1795 wnck_pager_motion (GtkWidget        *widget,
1796                    GdkEventMotion   *event)
1797 {
1798   WnckPager *pager;
1799   GdkWindow *window;
1800   int x, y;
1801 
1802   pager = WNCK_PAGER (widget);
1803 
1804   window = gtk_widget_get_window (widget);
1805   gdk_window_get_pointer (window, &x, &y, NULL);
1806 
1807   if (!pager->priv->dragging &&
1808       pager->priv->drag_window != NULL &&
1809       gtk_drag_check_threshold (widget,
1810                                 pager->priv->drag_start_x,
1811                                 pager->priv->drag_start_y,
1812                                 x, y))
1813     {
1814       GdkDragContext *context;
1815       context = gtk_drag_begin (widget,
1816 				gtk_drag_dest_get_target_list (widget),
1817 				GDK_ACTION_MOVE,
1818 				1, (GdkEvent *)event);
1819       pager->priv->dragging = TRUE;
1820       pager->priv->prelight_dnd = TRUE;
1821       _wnck_window_set_as_drag_icon (pager->priv->drag_window,
1822 				     context,
1823 				     GTK_WIDGET (pager));
1824     }
1825 
1826   wnck_pager_check_prelight (pager, x, y, pager->priv->prelight_dnd);
1827 
1828   return TRUE;
1829 }
1830 
1831 static gboolean
wnck_pager_leave_notify(GtkWidget * widget,GdkEventCrossing * event)1832 wnck_pager_leave_notify	 (GtkWidget          *widget,
1833 	      		  GdkEventCrossing   *event)
1834 {
1835   WnckPager *pager;
1836 
1837   pager = WNCK_PAGER (widget);
1838 
1839   wnck_pager_check_prelight (pager, -1, -1, FALSE);
1840 
1841   return FALSE;
1842 }
1843 
1844 static gboolean
wnck_pager_button_release(GtkWidget * widget,GdkEventButton * event)1845 wnck_pager_button_release (GtkWidget        *widget,
1846                            GdkEventButton   *event)
1847 {
1848   WnckWorkspace *space;
1849   WnckPager *pager;
1850   int i;
1851   int j;
1852   int viewport_x;
1853   int viewport_y;
1854 
1855   if (event->button != 1)
1856     return FALSE;
1857 
1858   pager = WNCK_PAGER (widget);
1859 
1860   if (!pager->priv->dragging)
1861     {
1862       i = workspace_at_point (pager,
1863                               event->x, event->y,
1864                               &viewport_x, &viewport_y);
1865       j = workspace_at_point (pager,
1866                               pager->priv->drag_start_x,
1867                               pager->priv->drag_start_y,
1868                               NULL, NULL);
1869 
1870       if (i == j && i >= 0 &&
1871           (space = wnck_screen_get_workspace (pager->priv->screen, i)))
1872         {
1873           int screen_width, screen_height;
1874 
1875           /* Don't switch the desktop if we're already there */
1876           if (space != wnck_screen_get_active_workspace (pager->priv->screen))
1877             wnck_workspace_activate (space, event->time);
1878 
1879           /* EWMH only lets us move the viewport for the active workspace,
1880            * but we just go ahead and hackily assume that the activate
1881            * just above takes effect prior to moving the viewport
1882            */
1883 
1884           /* Transform the pointer location to viewport origin, assuming
1885            * that we want the nearest "regular" viewport containing the
1886            * pointer.
1887            */
1888           screen_width  = wnck_screen_get_width  (pager->priv->screen);
1889           screen_height = wnck_screen_get_height (pager->priv->screen);
1890           viewport_x = (viewport_x / screen_width)  * screen_width;
1891           viewport_y = (viewport_y / screen_height) * screen_height;
1892 
1893           if (wnck_workspace_get_viewport_x (space) != viewport_x ||
1894               wnck_workspace_get_viewport_y (space) != viewport_y)
1895             wnck_screen_move_viewport (pager->priv->screen, viewport_x, viewport_y);
1896         }
1897 
1898       wnck_pager_clear_drag (pager);
1899     }
1900 
1901   return FALSE;
1902 }
1903 
1904 static gboolean
wnck_pager_focus(GtkWidget * widget,GtkDirectionType direction)1905 wnck_pager_focus (GtkWidget        *widget,
1906                   GtkDirectionType  direction)
1907 {
1908   WnckPager *pager;
1909 
1910   pager = WNCK_PAGER (widget);
1911 
1912   return GTK_WIDGET_CLASS (wnck_pager_parent_class)->focus (widget, direction);
1913 }
1914 
1915 /**
1916  * wnck_pager_set_screen:
1917  * @pager: a #WnckPager.
1918  * @screen: a #WnckScreen.
1919  *
1920  * Does nothing.
1921  *
1922  * Since: 2.2
1923  * Deprecated:2.20:
1924  */
1925 void
wnck_pager_set_screen(WnckPager * pager,WnckScreen * screen)1926 wnck_pager_set_screen (WnckPager  *pager,
1927 		       WnckScreen *screen)
1928 {
1929 }
1930 
1931 static gboolean
wnck_pager_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip)1932 wnck_pager_query_tooltip (GtkWidget  *widget,
1933                           gint        x,
1934                           gint        y,
1935                           gboolean    keyboard_tip,
1936                           GtkTooltip *tooltip)
1937 {
1938   int i;
1939   WnckPager *pager;
1940   WnckScreen *screen;
1941   WnckWorkspace *space;
1942   char *name;
1943 
1944   pager = WNCK_PAGER (widget);
1945   screen = pager->priv->screen;
1946 
1947   i = workspace_at_point (pager, x, y, NULL, NULL);
1948   space = wnck_screen_get_workspace (screen, i);
1949   if (!space)
1950     return GTK_WIDGET_CLASS (wnck_pager_parent_class)->query_tooltip (widget,
1951                                                                       x, y,
1952                                                                       keyboard_tip,
1953                                                                       tooltip);
1954 
1955   if (wnck_screen_get_active_workspace (screen) == space)
1956     {
1957       WnckWindow *window;
1958       GdkRectangle workspace_rect;
1959 
1960       get_workspace_rect (pager, i, &workspace_rect);
1961 
1962       window = window_at_point (pager, space, &workspace_rect, x, y);
1963 
1964       if (window)
1965         name = g_strdup_printf (_("Click to start dragging \"%s\""),
1966                                 wnck_window_get_name (window));
1967       else
1968         name = g_strdup_printf (_("Current workspace: \"%s\""),
1969                                 wnck_workspace_get_name (space));
1970     }
1971   else
1972     {
1973       name = g_strdup_printf (_("Click to switch to \"%s\""),
1974                               wnck_workspace_get_name (space));
1975     }
1976 
1977   gtk_tooltip_set_text (tooltip, name);
1978 
1979   g_free (name);
1980 
1981   return TRUE;
1982 }
1983 
1984 /**
1985  * wnck_pager_new:
1986  * @screen: deprecated argument, can be %NULL.
1987  *
1988  * Creates a new #WnckPager. The #WnckPager will show the #WnckWorkspace of the
1989  * #WnckScreen it is on.
1990  *
1991  * Return value: a newly created #WnckPager.
1992  */
1993 /* TODO: when we break API again, remove the screen from here */
1994 GtkWidget*
wnck_pager_new(WnckScreen * screen)1995 wnck_pager_new (WnckScreen *screen)
1996 {
1997   WnckPager *pager;
1998 
1999   pager = g_object_new (WNCK_TYPE_PAGER, NULL);
2000 
2001   return GTK_WIDGET (pager);
2002 }
2003 
2004 static gboolean
wnck_pager_set_layout_hint(WnckPager * pager)2005 wnck_pager_set_layout_hint (WnckPager *pager)
2006 {
2007   int layout_rows;
2008   int layout_cols;
2009 
2010   /* if we're not realized, we don't know about our screen yet */
2011   if (pager->priv->screen == NULL)
2012     _wnck_pager_set_screen (pager);
2013   /* can still happen if the pager was not added to a widget hierarchy */
2014   if (pager->priv->screen == NULL)
2015     return FALSE;
2016 
2017   /* The visual representation of the pager doesn't
2018    * correspond to the layout of the workspaces
2019    * here. i.e. the user will not pay any attention
2020    * to the n_rows setting on this pager.
2021    */
2022   if (!pager->priv->show_all_workspaces)
2023     return FALSE;
2024 
2025   if (pager->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2026     {
2027       layout_rows = pager->priv->n_rows;
2028       layout_cols = 0;
2029     }
2030   else
2031     {
2032       layout_rows = 0;
2033       layout_cols = pager->priv->n_rows;
2034     }
2035 
2036   pager->priv->layout_manager_token =
2037     wnck_screen_try_set_workspace_layout (pager->priv->screen,
2038                                           pager->priv->layout_manager_token,
2039                                           layout_rows,
2040                                           layout_cols);
2041 
2042   return (pager->priv->layout_manager_token != WNCK_NO_MANAGER_TOKEN);
2043 }
2044 
2045 /**
2046  * wnck_pager_set_orientation:
2047  * @pager: a #WnckPager.
2048  * @orientation: orientation to use for the layout of #WnckWorkspace on the
2049  * #WnckScreen @pager is watching.
2050  *
2051  * Tries to change the orientation of the layout of #WnckWorkspace on the
2052  * #WnckScreen @pager is watching. Since no more than one application should
2053  * set this property of a #WnckScreen at a time, setting the layout is not
2054  * guaranteed to work.
2055  *
2056  * If @orientation is %GTK_ORIENTATION_HORIZONTAL, the #WnckWorkspace will be
2057  * laid out in rows, with the first #WnckWorkspace in the top left corner.
2058  *
2059  * If @orientation is %GTK_ORIENTATION_VERTICAL, the #WnckWorkspace will be
2060  * laid out in columns, with the first #WnckWorkspace in the top left corner.
2061  *
2062  * For example, if the layout contains one row, but the orientation of the
2063  * layout is vertical, the #WnckPager will display a column of #WnckWorkspace.
2064  *
2065  * If @pager has not been added to a widget hierarchy, the call will fail
2066  * because @pager can't know the screen on which to modify the orientation.
2067  *
2068  * Return value: %TRUE if the layout of #WnckWorkspace has been successfully
2069  * changed or did not need to be changed, %FALSE otherwise.
2070  */
2071 gboolean
wnck_pager_set_orientation(WnckPager * pager,GtkOrientation orientation)2072 wnck_pager_set_orientation (WnckPager     *pager,
2073                             GtkOrientation orientation)
2074 {
2075   GtkOrientation old_orientation;
2076   gboolean       old_orientation_is_valid;
2077 
2078   g_return_val_if_fail (WNCK_IS_PAGER (pager), FALSE);
2079 
2080   if (pager->priv->orientation == orientation)
2081     return TRUE;
2082 
2083   old_orientation = pager->priv->orientation;
2084   old_orientation_is_valid = pager->priv->screen != NULL;
2085 
2086   pager->priv->orientation = orientation;
2087 
2088   if (wnck_pager_set_layout_hint (pager))
2089     {
2090       gtk_widget_queue_resize (GTK_WIDGET (pager));
2091       return TRUE;
2092     }
2093   else
2094     {
2095       if (old_orientation_is_valid)
2096         pager->priv->orientation = old_orientation;
2097       return FALSE;
2098     }
2099 }
2100 
2101 /**
2102  * wnck_pager_set_n_rows:
2103  * @pager: a #WnckPager.
2104  * @n_rows: the number of rows to use for the layout of #WnckWorkspace on the
2105  * #WnckScreen @pager is watching.
2106  *
2107  * Tries to change the number of rows in the layout of #WnckWorkspace on the
2108  * #WnckScreen @pager is watching. Since no more than one application should
2109  * set this property of a #WnckScreen at a time, setting the layout is not
2110  * guaranteed to work.
2111  *
2112  * If @pager has not been added to a widget hierarchy, the call will fail
2113  * because @pager can't know the screen on which to modify the layout.
2114  *
2115  * Return value: %TRUE if the layout of #WnckWorkspace has been successfully
2116  * changed or did not need to be changed, %FALSE otherwise.
2117  */
2118 gboolean
wnck_pager_set_n_rows(WnckPager * pager,int n_rows)2119 wnck_pager_set_n_rows (WnckPager *pager,
2120 		       int        n_rows)
2121 {
2122   int      old_n_rows;
2123   gboolean old_n_rows_is_valid;
2124 
2125   g_return_val_if_fail (WNCK_IS_PAGER (pager), FALSE);
2126   g_return_val_if_fail (n_rows > 0, FALSE);
2127 
2128   if (pager->priv->n_rows == n_rows)
2129     return TRUE;
2130 
2131   old_n_rows = pager->priv->n_rows;
2132   old_n_rows_is_valid = pager->priv->screen != NULL;
2133 
2134   pager->priv->n_rows = n_rows;
2135 
2136   if (wnck_pager_set_layout_hint (pager))
2137     {
2138       gtk_widget_queue_resize (GTK_WIDGET (pager));
2139       return TRUE;
2140     }
2141   else
2142     {
2143       if (old_n_rows_is_valid)
2144         pager->priv->n_rows = old_n_rows;
2145       return FALSE;
2146     }
2147 }
2148 
2149 /**
2150  * wnck_pager_set_display_mode:
2151  * @pager: a #WnckPager.
2152  * @mode: a display mode.
2153  *
2154  * Sets the display mode for @pager to @mode.
2155  */
2156 void
wnck_pager_set_display_mode(WnckPager * pager,WnckPagerDisplayMode mode)2157 wnck_pager_set_display_mode (WnckPager            *pager,
2158 			     WnckPagerDisplayMode  mode)
2159 {
2160   g_return_if_fail (WNCK_IS_PAGER (pager));
2161 
2162   if (pager->priv->display_mode == mode)
2163     return;
2164 
2165   g_object_set (pager, "has-tooltip", mode != WNCK_PAGER_DISPLAY_NAME, NULL);
2166 
2167   pager->priv->display_mode = mode;
2168   gtk_widget_queue_resize (GTK_WIDGET (pager));
2169 }
2170 
2171 /**
2172  * wnck_pager_set_show_all:
2173  * @pager: a #WnckPager.
2174  * @show_all_workspaces: whether to display all #WnckWorkspace in @pager.
2175  *
2176  * Sets @pager to display all #WnckWorkspace or not, according to
2177  * @show_all_workspaces.
2178  */
2179 void
wnck_pager_set_show_all(WnckPager * pager,gboolean show_all_workspaces)2180 wnck_pager_set_show_all (WnckPager *pager,
2181 			 gboolean   show_all_workspaces)
2182 {
2183   g_return_if_fail (WNCK_IS_PAGER (pager));
2184 
2185   show_all_workspaces = (show_all_workspaces != 0);
2186 
2187   if (pager->priv->show_all_workspaces == show_all_workspaces)
2188     return;
2189 
2190   pager->priv->show_all_workspaces = show_all_workspaces;
2191   gtk_widget_queue_resize (GTK_WIDGET (pager));
2192 }
2193 
2194 /**
2195  * wnck_pager_set_shadow_type:
2196  * @pager: a #WnckPager.
2197  * @shadow_type: a shadow type.
2198  *
2199  * Sets the shadow type for @pager to @shadow_type. The main use of this
2200  * function is proper integration of #WnckPager in panels with non-system
2201  * backgrounds.
2202  *
2203  * Since: 2.2
2204  */
2205 void
wnck_pager_set_shadow_type(WnckPager * pager,GtkShadowType shadow_type)2206 wnck_pager_set_shadow_type (WnckPager *   pager,
2207 			    GtkShadowType shadow_type)
2208 {
2209   g_return_if_fail (WNCK_IS_PAGER (pager));
2210 
2211   if (pager->priv->shadow_type == shadow_type)
2212     return;
2213 
2214   pager->priv->shadow_type = shadow_type;
2215   gtk_widget_queue_resize (GTK_WIDGET (pager));
2216 }
2217 
2218 static void
active_window_changed_callback(WnckScreen * screen,WnckWindow * previous_window,gpointer data)2219 active_window_changed_callback    (WnckScreen      *screen,
2220                                    WnckWindow      *previous_window,
2221                                    gpointer         data)
2222 {
2223   WnckPager *pager = WNCK_PAGER (data);
2224   gtk_widget_queue_draw (GTK_WIDGET (pager));
2225 }
2226 
2227 static void
active_workspace_changed_callback(WnckScreen * screen,WnckWorkspace * previous_workspace,gpointer data)2228 active_workspace_changed_callback (WnckScreen      *screen,
2229                                    WnckWorkspace   *previous_workspace,
2230                                    gpointer         data)
2231 {
2232   WnckPager *pager = WNCK_PAGER (data);
2233   gtk_widget_queue_draw (GTK_WIDGET (pager));
2234 }
2235 
2236 static void
window_stacking_changed_callback(WnckScreen * screen,gpointer data)2237 window_stacking_changed_callback  (WnckScreen      *screen,
2238                                    gpointer         data)
2239 {
2240   WnckPager *pager = WNCK_PAGER (data);
2241   gtk_widget_queue_draw (GTK_WIDGET (pager));
2242 }
2243 
2244 static void
window_opened_callback(WnckScreen * screen,WnckWindow * window,gpointer data)2245 window_opened_callback            (WnckScreen      *screen,
2246                                    WnckWindow      *window,
2247                                    gpointer         data)
2248 {
2249   WnckPager *pager = WNCK_PAGER (data);
2250 
2251   wnck_pager_connect_window (pager, window);
2252   wnck_pager_queue_draw_window (pager, window);
2253 }
2254 
2255 static void
window_closed_callback(WnckScreen * screen,WnckWindow * window,gpointer data)2256 window_closed_callback            (WnckScreen      *screen,
2257                                    WnckWindow      *window,
2258                                    gpointer         data)
2259 {
2260   WnckPager *pager = WNCK_PAGER (data);
2261 
2262   if (pager->priv->drag_window == window)
2263     wnck_pager_clear_drag (pager);
2264 
2265   wnck_pager_queue_draw_window (pager, window);
2266 }
2267 
2268 static void
workspace_created_callback(WnckScreen * screen,WnckWorkspace * space,gpointer data)2269 workspace_created_callback        (WnckScreen      *screen,
2270                                    WnckWorkspace   *space,
2271                                    gpointer         data)
2272 {
2273   WnckPager *pager = WNCK_PAGER (data);
2274   g_signal_connect (space, "name_changed",
2275                     G_CALLBACK (workspace_name_changed_callback), pager);
2276   gtk_widget_queue_resize (GTK_WIDGET (pager));
2277 }
2278 
2279 static void
workspace_destroyed_callback(WnckScreen * screen,WnckWorkspace * space,gpointer data)2280 workspace_destroyed_callback      (WnckScreen      *screen,
2281                                    WnckWorkspace   *space,
2282                                    gpointer         data)
2283 {
2284   WnckPager *pager = WNCK_PAGER (data);
2285   g_signal_handlers_disconnect_by_func (space, G_CALLBACK (workspace_name_changed_callback), pager);
2286   gtk_widget_queue_resize (GTK_WIDGET (pager));
2287 }
2288 
2289 static void
application_opened_callback(WnckScreen * screen,WnckApplication * app,gpointer data)2290 application_opened_callback       (WnckScreen      *screen,
2291                                    WnckApplication *app,
2292                                    gpointer         data)
2293 {
2294   /*   WnckPager *pager = WNCK_PAGER (data); */
2295 }
2296 
2297 static void
application_closed_callback(WnckScreen * screen,WnckApplication * app,gpointer data)2298 application_closed_callback       (WnckScreen      *screen,
2299                                    WnckApplication *app,
2300                                    gpointer         data)
2301 {
2302   /*   WnckPager *pager = WNCK_PAGER (data); */
2303 }
2304 
2305 static void
window_name_changed_callback(WnckWindow * window,gpointer data)2306 window_name_changed_callback      (WnckWindow      *window,
2307                                    gpointer         data)
2308 {
2309   WnckPager *pager = WNCK_PAGER (data);
2310   wnck_pager_queue_draw_window (pager, window);
2311 }
2312 
2313 static void
window_state_changed_callback(WnckWindow * window,WnckWindowState changed,WnckWindowState new,gpointer data)2314 window_state_changed_callback     (WnckWindow      *window,
2315                                    WnckWindowState  changed,
2316                                    WnckWindowState  new,
2317                                    gpointer         data)
2318 {
2319   WnckPager *pager = WNCK_PAGER (data);
2320 
2321   /* if the changed state changes the visibility in the pager, we need to
2322    * redraw the whole workspace. wnck_pager_queue_draw_window() might not be
2323    * enough */
2324   if (!wnck_pager_window_state_is_relevant (changed))
2325     wnck_pager_queue_draw_workspace (pager,
2326                                      wnck_pager_window_get_workspace (window,
2327                                                                       FALSE));
2328   else
2329     wnck_pager_queue_draw_window (pager, window);
2330 }
2331 
2332 static void
window_workspace_changed_callback(WnckWindow * window,gpointer data)2333 window_workspace_changed_callback (WnckWindow      *window,
2334                                    gpointer         data)
2335 {
2336   WnckPager *pager = WNCK_PAGER (data);
2337   gtk_widget_queue_draw (GTK_WIDGET (pager));
2338 }
2339 
2340 static void
window_icon_changed_callback(WnckWindow * window,gpointer data)2341 window_icon_changed_callback      (WnckWindow      *window,
2342                                    gpointer         data)
2343 {
2344   WnckPager *pager = WNCK_PAGER (data);
2345   wnck_pager_queue_draw_window (pager, window);
2346 }
2347 
2348 static void
window_geometry_changed_callback(WnckWindow * window,gpointer data)2349 window_geometry_changed_callback  (WnckWindow      *window,
2350                                    gpointer         data)
2351 {
2352   WnckPager *pager = WNCK_PAGER (data);
2353 
2354   wnck_pager_queue_draw_window (pager, window);
2355 }
2356 
2357 static void
background_changed_callback(WnckWindow * window,gpointer data)2358 background_changed_callback (WnckWindow *window,
2359                              gpointer    data)
2360 {
2361   WnckPager *pager = WNCK_PAGER (data);
2362 
2363   if (pager->priv->bg_cache)
2364     {
2365       g_object_unref (G_OBJECT (pager->priv->bg_cache));
2366       pager->priv->bg_cache = NULL;
2367     }
2368 
2369   gtk_widget_queue_draw (GTK_WIDGET (pager));
2370 }
2371 
2372 static void
workspace_name_changed_callback(WnckWorkspace * space,gpointer data)2373 workspace_name_changed_callback (WnckWorkspace *space,
2374                                  gpointer       data)
2375 {
2376   gtk_widget_queue_resize (GTK_WIDGET (data));
2377 }
2378 
2379 static void
viewports_changed_callback(WnckWorkspace * space,gpointer data)2380 viewports_changed_callback (WnckWorkspace *space,
2381                             gpointer       data)
2382 {
2383   gtk_widget_queue_resize (GTK_WIDGET (data));
2384 }
2385 
2386 static void
wnck_pager_connect_screen(WnckPager * pager)2387 wnck_pager_connect_screen (WnckPager *pager)
2388 {
2389   int i;
2390   guint *c;
2391   GList *tmp;
2392   WnckScreen *screen;
2393 
2394   g_return_if_fail (pager->priv->screen != NULL);
2395 
2396   screen = pager->priv->screen;
2397 
2398   for (tmp = wnck_screen_get_windows (screen); tmp; tmp = tmp->next)
2399     {
2400       wnck_pager_connect_window (pager, WNCK_WINDOW (tmp->data));
2401     }
2402 
2403   i = 0;
2404   c = pager->priv->screen_connections;
2405 
2406   c[i] = g_signal_connect (G_OBJECT (screen), "active_window_changed",
2407                            G_CALLBACK (active_window_changed_callback),
2408                            pager);
2409   ++i;
2410 
2411   c[i] = g_signal_connect (G_OBJECT (screen), "active_workspace_changed",
2412                            G_CALLBACK (active_workspace_changed_callback),
2413                            pager);
2414   ++i;
2415 
2416   c[i] = g_signal_connect (G_OBJECT (screen), "window_stacking_changed",
2417                            G_CALLBACK (window_stacking_changed_callback),
2418                            pager);
2419   ++i;
2420 
2421   c[i] = g_signal_connect (G_OBJECT (screen), "window_opened",
2422                            G_CALLBACK (window_opened_callback),
2423                            pager);
2424   ++i;
2425 
2426   c[i] = g_signal_connect (G_OBJECT (screen), "window_closed",
2427                            G_CALLBACK (window_closed_callback),
2428                            pager);
2429   ++i;
2430 
2431   c[i] = g_signal_connect (G_OBJECT (screen), "workspace_created",
2432                            G_CALLBACK (workspace_created_callback),
2433                            pager);
2434   ++i;
2435 
2436   c[i] = g_signal_connect (G_OBJECT (screen), "workspace_destroyed",
2437                            G_CALLBACK (workspace_destroyed_callback),
2438                            pager);
2439   ++i;
2440 
2441   c[i] = g_signal_connect (G_OBJECT (screen), "application_opened",
2442                            G_CALLBACK (application_opened_callback),
2443                            pager);
2444   ++i;
2445 
2446   c[i] = g_signal_connect (G_OBJECT (screen), "application_closed",
2447                            G_CALLBACK (application_closed_callback),
2448                            pager);
2449   ++i;
2450 
2451   c[i] = g_signal_connect (G_OBJECT (screen), "background_changed",
2452                            G_CALLBACK (background_changed_callback),
2453                            pager);
2454   ++i;
2455 
2456   c[i] = g_signal_connect (G_OBJECT (screen), "viewports_changed",
2457                            G_CALLBACK (viewports_changed_callback),
2458                            pager);
2459   ++i;
2460 
2461   g_assert (i == N_SCREEN_CONNECTIONS);
2462 
2463   /* connect to name_changed on each workspace */
2464   for (i = 0; i < wnck_screen_get_workspace_count (pager->priv->screen); i++)
2465     {
2466       WnckWorkspace *space;
2467       space = wnck_screen_get_workspace (pager->priv->screen, i);
2468       g_signal_connect (space, "name_changed",
2469                         G_CALLBACK (workspace_name_changed_callback), pager);
2470     }
2471 }
2472 
2473 static void
wnck_pager_connect_window(WnckPager * pager,WnckWindow * window)2474 wnck_pager_connect_window (WnckPager  *pager,
2475                            WnckWindow *window)
2476 {
2477   g_signal_connect (G_OBJECT (window), "name_changed",
2478                     G_CALLBACK (window_name_changed_callback),
2479                     pager);
2480   g_signal_connect (G_OBJECT (window), "state_changed",
2481                     G_CALLBACK (window_state_changed_callback),
2482                     pager);
2483   g_signal_connect (G_OBJECT (window), "workspace_changed",
2484                     G_CALLBACK (window_workspace_changed_callback),
2485                     pager);
2486   g_signal_connect (G_OBJECT (window), "icon_changed",
2487                     G_CALLBACK (window_icon_changed_callback),
2488                     pager);
2489   g_signal_connect (G_OBJECT (window), "geometry_changed",
2490                     G_CALLBACK (window_geometry_changed_callback),
2491                     pager);
2492 }
2493 
2494 static void
wnck_pager_disconnect_screen(WnckPager * pager)2495 wnck_pager_disconnect_screen (WnckPager  *pager)
2496 {
2497   int i;
2498   GList *tmp;
2499 
2500   if (pager->priv->screen == NULL)
2501     return;
2502 
2503   i = 0;
2504   while (i < N_SCREEN_CONNECTIONS)
2505     {
2506       if (pager->priv->screen_connections[i] != 0)
2507         g_signal_handler_disconnect (G_OBJECT (pager->priv->screen),
2508                                      pager->priv->screen_connections[i]);
2509 
2510       pager->priv->screen_connections[i] = 0;
2511 
2512       ++i;
2513     }
2514 
2515   for (i = 0; i < wnck_screen_get_workspace_count (pager->priv->screen); i++)
2516     {
2517       WnckWorkspace *space;
2518       space = wnck_screen_get_workspace (pager->priv->screen, i);
2519       g_signal_handlers_disconnect_by_func (space, G_CALLBACK (workspace_name_changed_callback), pager);
2520     }
2521 
2522   for (tmp = wnck_screen_get_windows (pager->priv->screen); tmp; tmp = tmp->next)
2523     {
2524       wnck_pager_disconnect_window (pager, WNCK_WINDOW (tmp->data));
2525     }
2526 }
2527 
2528 static void
wnck_pager_disconnect_window(WnckPager * pager,WnckWindow * window)2529 wnck_pager_disconnect_window (WnckPager  *pager,
2530                               WnckWindow *window)
2531 {
2532   g_signal_handlers_disconnect_by_func (G_OBJECT (window),
2533                                         G_CALLBACK (window_name_changed_callback),
2534                                         pager);
2535   g_signal_handlers_disconnect_by_func (G_OBJECT (window),
2536                                         G_CALLBACK (window_state_changed_callback),
2537                                         pager);
2538   g_signal_handlers_disconnect_by_func (G_OBJECT (window),
2539                                         G_CALLBACK (window_workspace_changed_callback),
2540                                         pager);
2541   g_signal_handlers_disconnect_by_func (G_OBJECT (window),
2542                                         G_CALLBACK (window_icon_changed_callback),
2543                                         pager);
2544   g_signal_handlers_disconnect_by_func (G_OBJECT (window),
2545                                         G_CALLBACK (window_geometry_changed_callback),
2546                                         pager);
2547 }
2548 
2549 static void
wnck_pager_clear_drag(WnckPager * pager)2550 wnck_pager_clear_drag (WnckPager *pager)
2551 {
2552   if (pager->priv->dragging)
2553     wnck_pager_queue_draw_window (pager, pager->priv->drag_window);
2554 
2555   pager->priv->dragging = FALSE;
2556   pager->priv->drag_window = NULL;
2557   pager->priv->drag_start_x = -1;
2558   pager->priv->drag_start_y = -1;
2559 }
2560 
2561 static GdkPixbuf*
wnck_pager_get_background(WnckPager * pager,int width,int height)2562 wnck_pager_get_background (WnckPager *pager,
2563                            int        width,
2564                            int        height)
2565 {
2566   Pixmap p;
2567   GdkPixbuf *pix = NULL;
2568 
2569   /* We have to be careful not to keep alternating between
2570    * width/height values, otherwise this would get really slow.
2571    */
2572   if (pager->priv->bg_cache &&
2573       gdk_pixbuf_get_width (pager->priv->bg_cache) == width &&
2574       gdk_pixbuf_get_height (pager->priv->bg_cache) == height)
2575     return pager->priv->bg_cache;
2576 
2577   if (pager->priv->bg_cache)
2578     {
2579       g_object_unref (G_OBJECT (pager->priv->bg_cache));
2580       pager->priv->bg_cache = NULL;
2581     }
2582 
2583   if (pager->priv->screen == NULL)
2584     return NULL;
2585 
2586   /* FIXME this just globally disables the thumbnailing feature */
2587   return NULL;
2588 
2589 #define MIN_BG_SIZE 10
2590 
2591   if (width < MIN_BG_SIZE || height < MIN_BG_SIZE)
2592     return NULL;
2593 
2594   p = wnck_screen_get_background_pixmap (pager->priv->screen);
2595 
2596   if (p != None)
2597     {
2598       _wnck_error_trap_push ();
2599       pix = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
2600                                               p,
2601                                               0, 0, 0, 0,
2602                                               -1, -1);
2603       _wnck_error_trap_pop ();
2604     }
2605 
2606   if (pix)
2607     {
2608       pager->priv->bg_cache = gdk_pixbuf_scale_simple (pix,
2609                                                        width,
2610                                                        height,
2611                                                        GDK_INTERP_BILINEAR);
2612 
2613       g_object_unref (G_OBJECT (pix));
2614     }
2615 
2616   return pager->priv->bg_cache;
2617 }
2618 
2619 /*
2620  *This will return aobj_pager whose parent is wnck's atk object -Gail Container
2621  */
2622 static AtkObject *
wnck_pager_get_accessible(GtkWidget * widget)2623 wnck_pager_get_accessible (GtkWidget *widget)
2624 {
2625   static gboolean first_time = TRUE;
2626 
2627   if (first_time)
2628     {
2629       AtkObjectFactory *factory;
2630       AtkRegistry *registry;
2631       GType derived_type;
2632       GType derived_atk_type;
2633 
2634       /*
2635        * Figure out whether accessibility is enabled by looking at the
2636        * type of the accessible object which would be created for
2637        * the parent type WnckPager.
2638        */
2639       derived_type = g_type_parent (WNCK_TYPE_PAGER);
2640 
2641       registry = atk_get_default_registry ();
2642       factory = atk_registry_get_factory (registry,
2643                                           derived_type);
2644       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2645 
2646       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
2647         {
2648           /*
2649            * Specify what factory to use to create accessible
2650            * objects
2651            */
2652           atk_registry_set_factory_type (registry,
2653                                          WNCK_TYPE_PAGER,
2654                                          WNCK_TYPE_PAGER_ACCESSIBLE_FACTORY);
2655 
2656           atk_registry_set_factory_type (registry,
2657                                          WNCK_TYPE_WORKSPACE,
2658                                          WNCK_TYPE_WORKSPACE_ACCESSIBLE_FACTORY);
2659         }
2660       first_time = FALSE;
2661     }
2662   return GTK_WIDGET_CLASS (wnck_pager_parent_class)->get_accessible (widget);
2663 }
2664 
2665 int
_wnck_pager_get_n_workspaces(WnckPager * pager)2666 _wnck_pager_get_n_workspaces (WnckPager *pager)
2667 {
2668   return wnck_screen_get_workspace_count (pager->priv->screen);
2669 }
2670 
2671 const char*
_wnck_pager_get_workspace_name(WnckPager * pager,int i)2672 _wnck_pager_get_workspace_name (WnckPager *pager,
2673                                 int        i)
2674 {
2675   WnckWorkspace *space;
2676 
2677   space = wnck_screen_get_workspace (pager->priv->screen, i);
2678   if (space)
2679     return wnck_workspace_get_name (space);
2680   else
2681     return NULL;
2682 }
2683 
2684 WnckWorkspace*
_wnck_pager_get_active_workspace(WnckPager * pager)2685 _wnck_pager_get_active_workspace (WnckPager *pager)
2686 {
2687   return wnck_screen_get_active_workspace (pager->priv->screen);
2688 }
2689 
2690 WnckWorkspace*
_wnck_pager_get_workspace(WnckPager * pager,int i)2691 _wnck_pager_get_workspace (WnckPager *pager,
2692                            int        i)
2693 {
2694   return wnck_screen_get_workspace (pager->priv->screen, i);
2695 }
2696 
2697 void
_wnck_pager_activate_workspace(WnckWorkspace * wspace,guint32 timestamp)2698 _wnck_pager_activate_workspace (WnckWorkspace *wspace,
2699                                 guint32        timestamp)
2700 {
2701   wnck_workspace_activate (wspace, timestamp);
2702 }
2703 
2704 void
_wnck_pager_get_workspace_rect(WnckPager * pager,int i,GdkRectangle * rect)2705 _wnck_pager_get_workspace_rect (WnckPager    *pager,
2706                                 int           i,
2707                                 GdkRectangle *rect)
2708 {
2709   get_workspace_rect (pager, i, rect);
2710 }
2711