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